1. 소개

Thymeleaf 는 HTML, XML, JavaScript, CSS 및 일반 텍스트를 처리하고 생성하기 위한 Java 템플릿 엔진입니다. Thymeleaf 및 Spring에 대한 소개는 이 글 을 살펴보십시오 .

이 기사에서는 Thymeleaf 애플리케이션을 사용하여 Spring MVC에서 CSRF(Cross-Site Request Forgery) 공격 을 방지하는 방법에 대해 설명합니다 . 보다 구체적으로 HTTP POST 방식에 대한 CSRF 공격을 테스트할 것입니다.

CSRF는 최종 사용자가 현재 인증된 웹 애플리케이션에서 원하지 않는 작업을 실행하도록 하는 공격입니다.

2. 메이븐 의존성

먼저 Thymeleaf와 Spring을 통합하는 데 필요한 구성을 살펴보겠습니다. thymeleaf -spring 라이브러리는 종속 항목에 필요합니다.

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>

Spring 4 프로젝트의 경우 thymeleaf-spring5 대신 thymeleaf -spring4  라이브러리를 사용해야 합니다 . 의존성의 최신 버전은 여기 에서 찾을 수 있습니다 .

또한 Spring Security를 ​​사용하기 위해서는 다음과 같은 의존성을 추가해야 합니다.

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.7.3</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.7.3</version>
</dependency>

두 가지 Spring Security 관련 라이브러리의 최신 버전은 여기여기 에서 사용할 수 있습니다 .

3. 자바 구성

여기 에서 다루는 Thymeleaf 구성 외에도 Spring Security에 대한 구성을 추가해야 합니다. 그러기 위해서는 다음과 같이 클래스를 만들어야 합니다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebMVCSecurity {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.withUsername("user1")
            .password("{noop}user1Pass")
            .authorities("ROLE_USER")
            .build();

        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring()
            .antMatchers("/resources/**");
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .authenticated()
            .and()
            .httpBasic();
        return http.build();
    }
}

Security 구성에 대한 자세한 내용과 설명은 Security with Spring 시리즈를 참조하십시오.

CSRF 보호는 Java 구성에서 기본적으로 활성화됩니다. 이 유용한 기능을 비활성화하려면 다음을 configure(…) 메서드에 추가해야 합니다.

.csrf().disable()

XML 구성에서 CSRF 보호를 수동으로 지정해야 합니다. 그렇지 않으면 작동하지 않습니다.

<security:http 
  auto-config="true"
  disable-url-rewriting="true" 
  use-expressions="true">
    <security:csrf />
     
    <!-- Remaining configuration ... -->
</security:http>

또한 로그인 양식이 있는 로그인 페이지를 사용하는 경우 코드에서 수동으로 숨겨진 매개변수로 로그인 양식에 CSRF 토큰을 항상 포함해야 합니다.

<input 
  type="hidden" 
  th:name="${_csrf.parameterName}" 
  th:value="${_csrf.token}" />

나머지 양식의 경우 숨겨진 입력이 있는 양식에 CSRF 토큰이 자동으로 추가됩니다.

<input 
  type="hidden" 
  name="_csrf"
  value="32e9ae18-76b9-4330-a8b6-08721283d048" /> 
<!-- Example token -->

4. 보기 구성

양식 작업 및 테스트 절차 작성이 포함된 HTML 파일의 주요 부분으로 진행하겠습니다. 첫 번째 보기에서 새 학생을 List에 추가하려고 합니다.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
    <h1>Add Student</h1>
        <form action="#" th:action="@{/saveStudent}" th:object="${student}"
          method="post">
            <ul>
                <li th:errors="*{id}" />
                <li th:errors="*{name}" />
                <li th:errors="*{gender}" />
                <li th:errors="*{percentage}" />
            </ul>
    <!-- Remaining part of HTML -->
    </form>
</body>
</html>

이 보기에서는 id , name , gender백분율 (선택적으로 양식 유효성 검사에 명시된 대로) 을 제공하여 학생을 List에 추가합니다 . 이 양식을 실행하기 전에 웹 애플리케이션에서 우리를 인증 하기 위해 사용자암호 를 제공해야 합니다.

4.1. 브라우저 CSRF 공격 테스트

이제 두 번째 HTML 보기로 진행합니다. 그 목적은 CSRF 공격을 시도하는 것입니다.

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<form action="http://localhost:8080/spring-thymeleaf/saveStudent" method="post">
    <input type="hidden" name="payload" value="CSRF attack!"/>
    <input type="submit" />
</form>
</body>
</html>

작업 URL은 http://localhost:8080/spring-thymeleaf/saveStudent 입니다. 해커는 공격을 수행하기 위해 이 페이지에 액세스하려고 합니다.

테스트하려면 애플리케이션에 로그인하지 않고 다른 브라우저에서 HTML 파일을 여십시오. 양식을 제출하려고 하면 다음 페이지가 표시됩니다.

즈르주트-에크라누

 

CSRF 토큰 없이 요청을 보냈기 때문에 요청이 거부되었습니다.

CSRF 토큰을 저장하기 위해 HTTP 세션이 사용된다는 점에 유의하십시오. 요청이 전송되면 Spring은 사용자가 해킹되지 않았는지 확인하기 위해 생성된 토큰과 세션에 저장된 토큰을 비교합니다.

4.2. JUnit CSRF 공격 테스트

브라우저를 사용하여 CSRF 공격을 테스트하고 싶지 않다면 빠른 통합 테스트를 통해 테스트할 수도 있습니다. 해당 테스트에 대한 Spring 구성부터 시작하겠습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { 
  WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class CsrfEnabledIntegrationTest {

    // configuration

}

그리고 실제 테스트로 이동합니다.

@Test
public void addStudentWithoutCSRF() throws Exception {
    mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON)
      .param("id", "1234567").param("name", "Joe").param("gender", "M")
      .with(testUser())).andExpect(status().isForbidden());
}

@Test
public void addStudentWithCSRF() throws Exception {
    mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON)
      .param("id", "1234567").param("name", "Joe").param("gender", "M")
      .with(testUser()).with(csrf())).andExpect(status().isOk());
}

첫 번째 테스트는 CSRF 토큰 누락으로 인해 금지 상태가 되는 반면 두 번째 테스트는 제대로 실행됩니다.

5. 결론

이 기사에서는 Spring Security 및 Thymeleaf 프레임워크를 사용하여 CSRF 공격을 방지하는 방법에 대해 설명했습니다.

이 사용방법(예제)의 전체 구현은 GitHub 프로젝트 에서 찾을 수 있습니다 .

Security footer banner