[Spring Security] - CSRF
CSRF (Cross-site request forgery, CSRF, XSRF)
CSRF란 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하도록 하는 공격을 말합니다. 공격의 난이도가 높지 않아 널리 사용되는 방법입니다.
2008년에 발생한 옥션의 개인정보 유출사건에서도 관리자 계정을 탈취하는데 이 방법이 사용되었습니다.
CSRF 공격 과정
- 이용자는 웹사이트에 로그인하여 정상적인 쿠키를 발급 받습니다.
- 공격자는 이메일이나 게시판 등의 경로를 통해 이용자에게 링크를 전달합니다.
- 공격용 HTML 페이지의 이미지 태그에는 이미지가 아닌 API를 호출할 수 있는 URL이 들어있습니다.
<a href="http://netbank.com/transfer.do?acct=AttackerA&amount=$100">Read more!</a>
- 이용자가 공격용 페이지를 열면, 브라우저는 이미지 파일을 받아오기 위해 공격용 URL을 열게 됩니다.
- 이용자의 승인이나 인지 없이 송급됨으로써 공격이 완료됩니다.
해당 서비스 페이지는 등록 과정에서 단순히 쿠키를 통한 본인확인 밖에 하지 않으므로 공격자가 정상 사용자 처럼 수정이 가능하게 됩니다. -
웹 사이트가 POST 요청만 사용하는 경우에는
<a>
태그는 사용할 수 없지만, javascript를 자동 실행하여<form>
태그를 전송할 수 있습니다.<body onload="document.forms[0].submit()"> <form action="http://netbank.com/transfer.do" method="POST"> <input type="hidden" name="acct" value="AttackerA"/> <input type="hidden" name="amount" value="$100"/> <input type="submit" value="View my pictures!"/> </form> </body>
CSRF 공격으로부터 보호
CSRF 공격이 가능한 이유는 피해자 웹사이트의 HTTP 요청과 공격자 웹사이트의 요청이 정확히 동일하기 때문입니다. 즉, 악의적인 웹사이트에서 오는 요청은 거부하고 정상적인 웹사이트에서 오는 요청만 허용할 방법이 없음을 의미합니다. CSRF 공격으로부터 보호하려면 두 요청을 구분할 수 있도록 악의적인 사이트에서 제공할 수 없는 요청이 있는지 확인해야 합니다.
➡️ Spring은 CSRF 공격으로부터 보호하는 두 가지 메커니즘을 제공합니다.
1. 동기화 토큰 패턴(Synchronizer Token Pattern)
CSRF 공격으로부터 보호하는 가장 보편적이고 포괄적인 방법은 Synchronizer Token Pattern을 사용하는 것입니다. 이 솔루션은 각 HTTP 요청에 세션 쿠키 외에도 CSRF 토큰이라는 보안 랜덤 생성 값이 HTTP 요청에 있어야 합니다.
- 동기화 토큰 요청 샘플
POST /transfer HTTP/1.1 Host: bank.example.com Cookie: JSESSIONID=randomid Content-Type: application/x-www-form-urlencoded amount=100.00&routingNumber=1234&account=9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721
2. 세션 쿠키에 동일 사이트 속성 지정
CSRF 공격으로부터 보호하는 다른 방법은 쿠키에 동일한 사이트 속성을 지정하는 것입니다. 서버는 외부 사이트에서 쿠키가 전송되지 않도록 쿠키를 설정할 때 동일한 사이트 속성을 지정할 수 있습니다.
- Spring Security는 세션 쿠키 작성을 직접 제어하지 않으므로 SameSite 특성을 지원하지 않습니다.
- Spring Session은 서블릿 기반 응용 프로그램에서 SameSite 속성을 지원합니다.
- Spring Framework의 CookieWebSessionIdResolver는 WebFlux 기반 응용 프로그램에서 SameSite 속성을 즉시 지원합니다.
- SameSite HTTP 응답 샘플
Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax
Spring Security
- 보통 CSRF 공격의 방어는 조회(GET) 데이터는 방어 대상에 두지 않고 상태를 수정하는 메서드(PATCH, POST, PUT, DELETE)에만 적용합니다.
- Spring Security에서는
@EnableWebSecurity
어노테이션을 지정할 경우 자동으로 CSRF 보호 기능이 활성화 됩니다.@Configuration @EnableWebSecurity public class ApplicaionSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
- CookieCsrfTokenRepository는 CSRF 토큰을 “XSRF-TOKEN”이라는 쿠키에 유지하고 AngularJS 규칙에 따라 “X-XSRF-TOKEN” 헤더에서 읽는 CsrfToken 저장소입니다.
- CookieCsrfTokenRepository 클래스 내부를 확인해보면 쿠키 네임, 파라미터 네임, 헤더 네임이 디폴트로 설정되어 있습니다.
public final class CookieCsrfTokenRepository implements CsrfTokenRepository { static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN"; static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN"; private String parameterName = "_csrf"; private String headerName = "X-XSRF-TOKEN"; private String cookieName = "XSRF-TOKEN"; private boolean cookieHttpOnly = true; ... }
withHttpOnlyFalse()
메서드를 통해 CookieHttpOnly의 값을 false로 설정합니다. 이는 서버 이외의 다른 사용자가 특수 쿠키에 액세스하는 것을 방지합니다.
-
GET 요청 CSRF 쿠키 확인하기
-
POST 요청 CSRF 값 보내기
CSRF 비활성화
http.csrf().disable();
CSRF(Cross-Site Request Forgery)는 “사이트 간 요청”이 발생하기 쉬운 웹에 대해 요청할 때 필요합니다.
이러한 애플리케이션은 보통 템플릿 엔진(Thymeleaf, JSP)등을 사용하여 서버 측에서 전체 HTML을 생성하는 구조입니다.
하지만 최신의 애플리케이션은 주로 REST API로 설계되어 대부분 JSON 방식으로 통신을 합니다.
REST API는 HTTP 형식을 따르기 때문에 무상태(stateless)이며 서버쪽의 세션이나 브라우저 쿠키에 의존하지 않습니다.
REST API의 특성에 따라 더 이상 CSRF와 관련이 없으므로 CSRF 공격을 받을 가능성이 존재하지 않습니다.
따라서 대부분의 현재 애플리케이션(API만 노출하는)의 경우 앤드포인트에 대해 CSRF를 비활성화하고 있습니다.