- 공부를 하며 강의 내용을 따라하다가
csrf().disable()
이라는 코드를 무의식적으로 따라했던 기억이 있습니다. - 당시에는 그냥 개발과 테스트에 방해가 되지 않도록 Spring Security에서 제공해주는 보안 기능을 잠시 비활성화시킨다는 개념으로 사용했으나, 시간이 지나고 우연히 이와 관련된 내용을 보니 그보다 주요한 목적이 있는 것 같아 이를 찾아서 정리해보았습니다.
그때 그 시절 코드...
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**", "/sort/**", "/board", "/upload/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc")
.failureHandler(userLoginFailHandler)
.defaultSuccessUrl("/");
return http.build();
}
}
💡 CSRF(Cross Site Request Forgery)
- CSRF 공격이란 웹 애플리케이션 취약점 중 하나로, 인터넷 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 만드는 공격입니다.
- 즉, CSRF를 통해 해커는 사용자의 권한을 도용하여 중요 기능을 실행하는 것이 가능합니다.
- 참고로, CSRF는 해커가 사용자의 컴퓨터를 감염시키거나 페이스북 서버를 해킹을 해서 이뤄지는 공격은 아닙니다. 그래서 CSRF 공격이 이뤄지려면 다음과 같은 조건이 만족되어야 합니다.
- 위조 요청을 전송하는 서비스에 사용자가 로그인 한 상태인 경우
- 사용자가 해커가 만든 피싱 사이트에 접속한 경우
- 보통 자주 사용하는 사이트의 경우 자동 로그인을 해놓는 경우가 많고, 해커가 만든 피싱 사이트가 아니라 XSS 공격을 성공한 정상 사이트를 통해서도 CSRF 공격이 수행될 수 있기 때문에 충분히 가능한 공격 기법이라고 할 수 있습니다.
💡 CSRF 공격 방어
- CSRF 공격을 막기 위한 방법으로는 대표적으로 두 가지가 있습니다.
- Referer 검증
- CSRF Token 검증
✔ Referer 검증
- Referer이란 현재 표시하는 웹 페이지가 어떤 웹 페이지에서 요청되었는지, 어떤 웹 페이지나 웹 서버에서 방문자가 왔는지를 확인할 수 있도록 해주는 HTTP Request Header의 요소를 말합니다.
- Referer 검증은 이를 활용하여 Back-end 단에서 Request의 Referrer를 확인하여 도메인(ex.
*.facebook.com
)이 일치하는지 검증하는 방법으로, 해당 요청이 적절한 위치에서 전달된 요청이 아닐 경우 예외를 발생시켜 이를 막습니다. - 일반적으로는 Referrer 검증만으로 대부분의 CSRF 공격을 방어할 수 있습니다.
- 다만 같은 도메인 내의 페이지에 XSS 취약점이 있는 경우 CSRF 공격에 취약해질 수 있습니다.
- 따라서 도메인 단위 검증에서 좀 더 세밀하게 페이지 단위까지 일치하는지 검증해야 도메인 내 타 페이지에서의 XSS 취약점에 의한 CSRF 공격까지 방어할 수 있습니다.
✔ CSRF Token 검증
- Referrer 검증이 불가한 환경이라면, CSRF Token를 활용할 수 있습니다.
- 우선 사용자의 세션에 임의의 난수 값을 저장하고 사용자의 요청마다 해당 난수 값을 포함시켜 전송시킵니다.
- 이후 Back-end 단에서 요청을 받을 때마다 세션에 저장된 토큰 값과 요청 파라미터에 전달되는 토큰 값이 일치하는 지 검증하여 적절한 요청인지 확인합니다.
- 이 방법도 결국 같은 도메인 내에 XSS 취약점이 있다면 CSRF 공격에 취약해집니다.
- 아래는 간략한 샘플 코드입니다.
// 로그인시, 또는 작업화면 요청시 CSRF 토큰을 생성하여 세션에 저장한다.
session.setAttribute("CSRF_TOKEN",UUID.randomUUID().toString());
// 요청 페이지에 CSRF 토큰을 셋팅하여 전송한다
<input type="hidden" name="_csrf" value="${CSRF_TOKEN}" />
// 파라미터로 전달된 csrf 토큰 값
String param = request.getParameter("_csrf");
// 세션에 저장된 토큰 값과 일치 여부 검증
if (request.getSession().getAttribute("CSRF_TOKEN").equals(param)) {
return true;
} else {
response.sendRedirect("/");
return false;
}
💡 CSRF 방어 기능을 마음대로 비활성화 해도 될까?
- 앞에서, 필자는 개발을 할 때 Spring Security에서 기본으로 제공해주는 CSRF 방어 기능을 비활성화 했다고 했습니다.
- Spring Security는 웹 사이트 취약점 공격을 방지하기 위해 CSRF 토큰 값을 세션을 통해 발행하고 웹 페이지에서는 폼 전송시에 해당 토큰을 함께 전송하여 실제 웹 페이지에서 작성된 데이터가 전달되는지를 검증하기 때문에, 개발시 테스트 수행을 위한 요청까지 모두 막아버리기 때문입니다.
- 그렇다고 보안 기능을 이렇게 마음대로 비활성화 해도 되는 걸까요?
- 공식 문서를 살펴보면 언제 CSRF protection 기능을 사용해야 하는지 명시되어 있습니다.
- 위 내용에서 CSRF protecton 기능은 브라우저를 통해 Request를 받을 때 사용하라고 적혀 있습니다.
- 즉, 브라우저를 통해 Request를 받지 않는다면 사용하지 않아도 된다고 해석할 수 있습니다.
- 따라서 SPA(Single Page Application) 방식으로 개발된 REST API 서버는 해당 기능을 비활성화해도 크게 상관이 없다고 할 수 있습니다.
- 💬 왜 그런가요?
- REST API 서버는 HTTP 형식을 따르기 때문에 무상태(stateless)이며, 때문에 서버쪽의 세션이나 브라우저 쿠키에 의존하지 않습니다.
- 일반적으로 JWT와 같은 토큰을 사용하여 인증하기 때문에 해당 토큰을 Cookie에 저장하지 않는다면 CSRF 취약점에 대해서는 이미 어느 정도 안전하다고 말할 수 있습니다.
- 💬 왜 그런가요?
- 정리하자면 세션을 사용할 때 발생하는 취약점인 CSRF에 대해서 100%라고 장담할 수는 없지만 취약점 발생 요소가 제거되었기 때문에 CSRF protection 기능을 비활성화 하더라도 큰 문제가 되지 않는 것입니다.
📌 References
'🥑 Web Technoloy' 카테고리의 다른 글
AJAX란? (0) | 2023.06.13 |
---|---|
RESTful API란? (0) | 2023.06.13 |
URI와 URL (0) | 2023.06.06 |
JPQL과 QueryDSL (0) | 2023.06.04 |
JPA와 Hibernate 그리고 Spring Data JPA (0) | 2023.06.04 |