CSRF(Cross-Site Request Forgery)는 웹 애플리케이션 보안에서 가장 빈번히 발견되는 취약점 중 하나입니다. 공격자는 사용자를 속여 원치 않는 요청을 서버에 보내도록 하며, 이는 사용자 계정 탈취나 권한 있는 작업의 악용으로 이어질 수 있습니다.
CSRF란?
CSRF(Cross Site Request Forgery)는 사용자의 인증 정보를 악용하여 악의적인 요청을 서버에 전송하는 공격입니다. 사용자가 신뢰하는 사이트와의 인증 상태를 활용해 비인가 작업을 실행하도록 유도합니다.
공격 시나리오

- 사용자가 http://bank.com에 로그인하고 세션이 활성화된 상태로 유지.
- 공격자는 사용자를 악성 사이트 http://malicious.com로 유도.
- 악성 사이트에 아래와 같은 코드가 포함되어 있음:
<form action="http://bank.com/transfer" method="POST"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="1000"> </form> <script> document.forms[0].submit(); </script>
- 사용자가 악성 사이트를 방문하면, http://bank.com으로 요청이 전송되어 인증된 사용자의 권한으로 공격자의 작업이 수행됨.
(사용자는 http://bank.com 세션이 활성화되어 있기 때문에 http://bank.com 요청이 수행됨.)
CSRF 방어 방법
Spring Security는 CSRF 공격을 방어하기 위한 CSRF 토큰 검증을 기본적으로 제공합니다.
1. Spring Security에서 CSRF 활성화
Spring Security에서는 CSRF 보호가 기본적으로 활성화되어 있습니다. 보호 설정이 적용되면 모든 POST, PUT, DELETE 요청에서 CSRF 토큰이 요구됩니다.
예제: 기본 CSRF 보호 설정
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout();
return http.build();
}
}
2. CSRF 토큰 사용 방법
Spring Security는 CSRF 토큰을 자동으로 생성하며, HTML 폼 또는 AJAX 요청에 포함할 수 있습니다.
HTML 폼에서 CSRF 토큰 삽입
Spring Boot에서 Thymeleaf를 사용하는 경우:
<form action="/transfer" method="POST">
<input type="text" name="amount" placeholder="Amount">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button type="submit">Transfer</button>
</form>
_csrf.parameterName
과 _csrf.token
은 Spring Security가 제공하는 CSRF 토큰 값입니다.
3. AJAX 요청에서 CSRF 토큰 추가
AJAX 요청을 보낼 때 CSRF 토큰을 헤더에 추가해야 합니다. Spring Security는 이를 위한 CSRF 토큰 값을 기본적으로 제공하는 CsrfToken 객체를 생성합니다.
예제: JavaScript로 AJAX 요청 시 CSRF 토큰 추가
<script>
const csrfToken = document.querySelector('meta[name="_csrf"]').content;
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content;
fetch('/transfer', {
method: 'POST',
headers: {
[csrfHeader]: csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ to: 'attacker', amount: 1000 })
})
.then(response => {
if (!response.ok) {
throw new Error('Request failed');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
</script>
Spring에서 CSRF 토큰을 뷰로 전달
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class TransferController {
@GetMapping("/transfer")
public String transferPage(CsrfToken csrfToken, Model model) {
model.addAttribute("_csrf", csrfToken);
return "transfer";
}
}
4. CSRF 예외 처리
일부 엔드포인트(API 등)에서 CSRF 검증을 비활성화해야 할 경우가 있습니다. 이때는 특정 URL 패턴에 대해 예외를 설정할 수 있습니다.
예제: 특정 경로 CSRF 비활성화
http.csrf()
.ignoringAntMatchers("/api/**"); // /api/** 경로에서는 CSRF 검증 비활성화
5. SameSite 쿠키 설정
SameSite 쿠키 속성을 설정하면 크로스사이트 요청에서 인증 쿠키가 전송되지 않도록 방지할 수 있습니다.
Spring Boot에서 SameSite 설정
Spring Security 5.5 이상에서는 SameSite 속성을 아래와 같이 설정합니다.
import org.springframework.boot.web.server.Cookie.SameSite;
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite(SameSite.STRICT.toString());
serializer.setUseHttpOnlyCookie(true);
serializer.setUseSecureCookie(true); // HTTPS에서만 쿠키 전송
return serializer;
}
6. Referer check
HTTP 요청 헤더 정보에서 Referer 정보를 확인합니다. Host와 Referer가 일치하는지 검증하여 공격을 방어할 수도 있습니다.

RESTful API에서 CSRF 방어
REST API에서는 CSRF 토큰 대신 Bearer 토큰 또는 CORS 설정으로 방어하는 것이 일반적입니다.
Bearer 토큰을 사용한 인증
http.csrf().disable() // REST API에서는 CSRF 비활성화
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt(); // JWT 인증
CSRF는 간단한 공격 기법으로도 사용자의 중요한 데이터를 탈취하거나 서버에 악의적인 요청을 실행할 수 있습니다.
CSRF 보호를 올바르게 설정하고, 필요에 따라 예외 처리나 추가적인 보안 조치를 결합하여 안전한 애플리케이션을 구축해야 합니다!
'Study😜' 카테고리의 다른 글
로컬에서 sonarqube 실행해보기 (0) | 2025.02.14 |
---|---|
Spring Security에서 /logout 동작 과정 (0) | 2025.01.24 |
Spring Security에서 WebSecurityConfigurerAdapter가 Deprecated된 이유와 새로운 구성 방법 (0) | 2024.12.28 |
[Docker] SpringBoot + MySQL + Docker 프로젝트 실행하기 (1) | 2024.09.17 |
AWS lambda에 Springboot 프로젝트를 Docker로 배포하기 (0) | 2024.09.07 |