티스토리 뷰

WebFlux로 프로젝트를 진행하던 중, Multipart로 전송시  Spring Security에 설정한 CSRF 관련 사항과 충돌이 발생하여 이에대한 대응책을 남긴다.

(invalid csrf token)

 

Multipart의 경우 FormData가 ServerRequest에 QueryParameter로 들어오지 않기 때문에,

클라이언트 - CSRF Filter 사이에 중개필터를 하나 두고 필터 순서를 변경하여 CSRF관련 작업을 처리해야 한다.

 

작업순서는 아래와 같다.

 

1. 중개필터 작성

2. Spring Security에 중개필터 추가 (Filter Order는 CSRF Filter 이전에 위치하도록 순서지정)

 

먼저 중개필터를 작성한다.

 

@Component
public class MultipartCsrfFilter implements WebFilter {
    
    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange,
                             WebFilterChain webFilterChain) {
        if(!HttpMethod.GET.equals(serverWebExchange.getRequest().getMethod())){
            return serverWebExchange.getMultipartData().flatMap(v -> {
                Map<String, Part> map = v.toSingleValueMap();
                if(map.get("_csrf") != null){
                    serverWebExchange.getRequest()
                        .mutate()
                        .header("X-CSRF-TOKEN", ((FormFieldPart) map.get("_csrf")).value())
                        .build();
                }
                return webFilterChain.filter(serverWebExchange);
            });
        }else{
            return webFilterChain.filter(serverWebExchange);
        }
    }
    
}

 

Security 설정시 별다른 설정을 하지 않았다면 default token repository인 WebSessionServerCsrfTokenRepository 클래스의 정보를 따라간다.

 

 - WebSessionServerCsrfTokenRepository

public class WebSessionServerCsrfTokenRepository implements ServerCsrfTokenRepository {
    private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
    private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN";
    private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = WebSessionServerCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
    private String parameterName = "_csrf";
    private String headerName = "X-CSRF-TOKEN";
    private String sessionAttributeName;

    ...
    
}

위 소스코드를 보다시피, default header name은 'X-CSRF-TOKEN'이며 paramater name은 '_csrf'이다.

변경사항이 있다면 그에따라 필터의 내용도 변경하면 될 것이다.

 

중개필터의 내용은 단순히 GET요청이 아니면서, _csrf로 들어온 parameter가 있을 경우 헤더에 CSRF값을 담아주고

다음필터로 넘기는 내용이다.

 

 

다음은 Security설정이다.

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@ComponentScan(basePackages = {........})
public class SecurityConfiguration {
    
    // custom manager
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http.authorizeExchange()
            ... ...
            .addFilterAt(new MultipartCsrfFilter(), SecurityWebFiltersOrder.FIRST)
            .csrf()
            .and()
            .build();
    }
    
}

ServerHttpSecurity의 addFilterAt 함수를 통해 중개필터를 등록한다.

이때 필터 순서는 가장 먼저 올 수 있도록 지정

 

위와같이 작성하고 실행하면 다음과 같이 프로세스가 흘러감을 알 수 있다.

 

 - MultipartCsrfFilter

Security에 등록한 순서에 맞게 중개필터가 위치해 있음을 알 수 있다.

 

request multipart data 중 csrf 매개변수가 있음을 알 수 있다. 해당 변수가 있으니, request header에 token정보를 넣고 다음 필터로 넘길것이다.

 

 

 - CsrfWebFilter

CSRF검증시 ServerWebExchange의 request header에 CSRF관련 부분이 포함되었음을 알 수 있다.

 

이렇게 WebFlux환경에서의 Multipart CSRF검증 로직은 끝이난다.

 

Security 설정 부분에서 Filter 순서를 변경하지 않았다면 CsrfWebFilter가 상위에 위치하기 때문에 403 status와 함께 invalid csrf token가 출력되며,

중개필터에서 헤더에 올바른 token name을 넣지 않았다면 마찬가지로 CsrfWebFilter에서 토큰을 찾지 못했으므로 invalid csrf token가 출력되게 된다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함