스프링 시큐리티를 이용한다면 csrf 설정을 통해서 보안설정을 간단히 구현할 수 있습니다.
implementation 'org.springframework.security:spring-security-taglibs:5.8.0'
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
...
현재 사용자의 권한을 바탕으로 페이지의 특정 부분을 조건부로 렌더링하는 데 사용됩니다.
<security:authorize access="!isAuthenticated()">
Login
</security:authorize>
<security:authorize access="isAuthenticated()">
Logout
</security:authorize>
<security:authorize access="hasRole('ADMIN')">
Manage Users
</security:authorize>
<security:authorize url="/userManagement">
<a href="/userManagement">Manage Users</a>
</security:authorize>
<sec:authorize access="isAuthenticated()">
Welcome Back, <sec:authentication property="name"/>
</sec:authorize>
<security:authorize access="!isAnonymous()">
이 태그는 현재 인증된 사용자의 상세 정보를 가져오는 데 사용됩니다.
<sec:authentication property="name"/>
<security:authentication property="principal.userName" var="userName"/>
CSRF 공격을 보호하기 위해 사용됩니다.
<head>
<security:csrfMetaTags/>
</head>
이 태그는 html 메타태그를 생성하게 되고 아래와 같은 형태입니다.
<meta name="_csrf" content="토큰값"/>
<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
이러한 토큰값은 자바스크립트의 요청에 포함해 보낼 수 있습니다.
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
const csrfHeaderName = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');
fetch('/some-endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
[csrfHeaderName]: csrfToken, // CSRF 토큰을 헤더에 추가
},
body: JSON.stringify({ data: 'yourData' }),
})
.then(response => response.json())
.then(data => console.log(data));
var csrfToken = $("meta[name='_csrf']").attr("content");
var csrfHeader = $("meta[name='_csrf_header']").attr("content");
$.ajax({
url: "your-endpoint",
type: "post",
data: { ... },
beforeSend: function(xhr) {
xhr.setRequestHeader(csrfHeader, csrfToken);
}
});
<div>
<security:csrfInput />
</div>
자동으로 아래 태그를 만들어 줍니다.
<input type="hidden" name="_csrf" value="토큰값"/>
POST요청시 form에 포함시켜서 요청을 보낼 수 있습니다.
스프링에서 아래와 같은 시큐리티 설정을 통해 로그인이 되었을 경우 헤더에 csrf 토큰을 넣어서 응답합니다.
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// .. 생략
);
return http.build();
}
http.csrf()를 통해 csrf 보호를 활성화하게 되면 HttpSessionCsrfTokenRepository가 디폴트로 설정됩니다.
이는 세션이 토큰을 저장하게 되고 클라이언트는 <security:csrfInput /> 또는 <security:csrfMetaTags/>를 통해 html에 포함된 토큰을 요청 헤더에 포함시킵니다.
이때 스프링 시큐리티의 CsrfFilter는 세션의 토큰과 헤더의 토큰이 일치하는지 검사하게 됩니다.
csrfTokenRepository(CookieCsrfTokenRepository) 설정은 CSRF 토큰을 쿠키에 저장하도록 합니다.

HttpOnlyTrue()로 설정하면 자바스크립트가 쿠키의 값을 가져올수 없어서 XSS(Cross-site scripting) 공격에 의한 쿠키 탈취를 방지할 수 있습니다.
하지만 html을 만들지 않고 클라이언트측에서 SPA로 화면을 만들경우 자바스크립트가 토큰값을 가져와서 비동기로 요청을 보내야할때가 있으므로 false설정을 합니다.
서버사이드렌더링이라면 false설정을 하지않아도 됩니다.
<security:csrfInput /> 또는 <security:csrfMetaTags/>로 html에 포함된 토큰을 자바스크립트가 헤더에 포함시키면
스프링 시큐리티의 CsrfFilter가 쿠키에 저장된 토큰과 자바스크립트로 추가한 토큰이 일치하는지 검사하게 됩니다.