본문 바로가기

WEB/Spring

Spring security6와 CSRF

사이드 프로젝트 개발 도중 ..

 

Ajax로 Delete 요청을 하는데 자꾸 access-denied가 떴다

 

처음에는 admin, user 권한 때문인줄 알았지만 Spring Security에서 CSRF 공격 방지를 하기 위한 설정 때문이었다.

 

 

CSRF란?

사이트 간 요청 위조(cross site request forgery)라는 뜻의 웹사이트 취약점 공격의 하나로, 사용자 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정웹사이트에 요청하게하는 공격이다

 

Spring Security와 CSRF

Spring Security는 기본적으로 CSRF 공격을 방지하기 위해 CSRF 토큰을 사용한다.

 

해당 프레임워크를 사용할때 form 태그에 내가 추가하지 않은 값이 들어 있는 경우있다.

바로 아래 사진의 input 태그 처럼 Spring Security가 CSRF 토큰을 넣었기 때문이다.

 

자동으로 넣어주니 참 좋은데 ..

Ajax 요청을 할 땐 헤더 값에 토큰을 임의로 넣어야지 사용할 수 있다 ㅠ ㅠ

(참고로 get 요청은 Spring Security에서 CSRF 토큰을 사용하지 않는다)

 

 

방법은 다음과 같다

 

1. SecurityConfig 설정 변경

@Configuration
//여기보세요
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {


    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                			...생략...
                            //여기보세요
                .csrf((csrf) -> csrf
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
                        
                        
                                     ...생략...

 

  • @EnableWebSecurity 추가
  •  csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) 추가 (쿠키에 csrf 토큰을 저장하는 설정)

쿠키는 개발자 도구에서 볼 수 있다. XSRF(잘렸지만..)가 쿠키에 저장된 것을 알 수 있다

 

 

Spring Security 6과 이전 버전이 많이 달라졌다고 한다.

혹시 이전 버전(Spring boot 3 이전)을 쓰고 있다면 webSecurityConfigurerAdapter를 상속하고 config 메서드로 해당 설정을 하면된다.

검색하면 참고할 블로그가 많을 것이다.

그러나 Spring boot 3 버전을 쓴다면 Spring Security6을 사용한다.

configure 대신 filterchain 메서드를 사용하고 또 람다식으로 표현하면 된다( <- 빨간줄의 원인)

 

 

2. html <head>에 meta 정보 입력

<head>
    <!--CSRF-->
    <meta name="_csrf" content="${_csrf.token}">
    <meta name="_csrf_header" content="${_csrf.headerName}">
</head>

 

  • meta 정보 추가하기

 

혹시 타임리프 레이아웃 기능을 쓰고 있다면 꼭 th:content로 하고 손해보는 일 없길 바래요 ..

 

 

3. AJax 헤더에 토큰 설정

$(document).ready(function () {
    $(document).on('click', '#deleteBtn', function() {
        var postId = $(this).data('id');
                if (confirm('정말로 이 게시물을 삭제하시겠습니까?')) {
                    
                    //여기보세요
                    var csrfToken = $('meta[name="_csrf"]').attr('content');
                    var csrfHeader = $('meta[name="_csrf_header"]').attr('content');

                    $.ajax({
                        url: '/admin/post/delete/' + postId,
                        type: 'DELETE',
                        beforeSend: function(xhr) {
                        	//여기보세요
                            // CSRF 토큰을 헤더에 포함
                            xhr.setRequestHeader(csrfHeader, csrfToken); // CSRF 토큰 설정 부분
                        },
                        success: function(result) {
                            ...생략...
                        },
                        error: function(xhr, status, error) {
                            ...생략...            }
        });
    }
});
});

 

  • xhr.setRequestHeader(csrfHeader, csrfToken)로 요청전에 헤더에 토큰 설정

 

이렇게 설정하면 Spring security를 사용하면서 post, delete로 Ajax 요청을 할 수 있다!!

 

CSRF 공격에 관하여 용어만 알고 정확히 무엇인지 몰랐는데 이번에 조금 알게 된 것 같다 ㅎㅎ

 

참고자료

'WEB > Spring' 카테고리의 다른 글

메세지, 국제화 인코딩 오류  (0) 2024.06.16
Enum과 Thymeleaf  (0) 2024.06.16
Spring security에서 static resources 파일 적용  (0) 2024.05.22
valid와 validation  (0) 2024.05.22
Entity와 DTO  (0) 2024.05.21