spring/spring-security

스프링 부트(3.1.5) 어플리케이션에서 CSRF 적용하기

ppthejake 2023. 10. 23. 12:29

Cross-Site Request Forgery(CSRF) 는 공격자가 사용자를 대신하여 무단으로 조작하는 흔한 웹 보안 취약점이다. Spring Boot는 Spring Security를 통하여 CSRF 공격으로부터 어플리케이션을 간단하게 보호할 수 있는 방법을 제공한다. 최근에 Security 관련설정에서 상속받아 사용하던 WebSecurityConfigurerAdapter 클래스가 Deprecate됨에 따라 설정방법에도 변화가 발생하였다.

Spring Boot Security 설정 2.X => 3.1.X 마이그레이션은 다음의 문서를 확인해 보자
https://docs.spring.io/spring-security/reference/migration-7/configuration.html

사전 구성 요소

스프링 부트 프로젝트를 구성할 때 Spring Initializr를 이용하여 다음의 디펜던시를 추가한다.

  • Spring Web
  • Spring Security

Step 1: Gradle Dependencies (build.gradle)

build.gradle 에 디펜던시가 다음과 같이 포함되어있는지 확인해보자. 

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

Step 2: 컨트롤러 설정

폼 화면 및 Submit 처리를 위한 컨트롤러를 간단하게 구성해보자.

@Controller
@RequestMapping("/sample")
public class SampleController {

    @GetMapping("/form")
    public String showForm() {
        return "csrf-form";
    }

    @PostMapping("/submit")
    public String processForm() {
        return "success";
    }
}

Step 3: HTML Form 구성

Thymeleaf 를 사용하여 CSRF 토큰을 포함하는 HTML form 을 생성한다. CSRF 토큰을 포함하게 되면 요청 이 어플리케이션에서 온 것인지를 확인한다. 간단히 구성해 보자. (thymeleaf 를 사용하는 경우 관련 디펜던시를 추가해야 한다.)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>CSRF Example - Form</title>
</head>
<body>
    <h1>CSRF Example</h1>

    <form action="/sample/submit" method="post">
        <label for="data">Data:</label>
        <input type="text" id="data" name="data">

        <!-- CSRF token 을 포함시킨다 -->
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">

        <button type="submit">Submit</button>
    </form>
</body>
</html>

Step 4: Security 설정하기

Spring Security 를 활성화하고 CSRF를 구성하려면 관련 설정을 추가해야 한다. 다음과 같이 구성해보자

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
            .authorizeHttpRequests(authorize -> authorize.requestMatchers(
                    "/sample/form"
                ).permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .csrf(csrf -> csrf.csrfTokenRepository(csrfTokenRepository()));

        return http.build();
    }
}

Step 5: 테스트

CSRF를 구현한 후에 어플리케이션을 테스트 하는것이 중요하다. Form 이 CSRF 토큰을 포함하는 지 확인하고, CSRF 토큰 유효성 검사가 정상적으로 작동하는지 확인해 보자.

 

Spring Security 에서 CSRF 대응 방법에 대하여 간단하게 살펴보았다.