Growth & Practice/Today I Learned

[TIL] Postman multipart 오류 해결 + CSRF, HttpOnly 쿠키 개념

foreverWon 2025. 9. 30. 11:54
목차

    트러블슈팅

    1. Postman에서 multipart/form-data로 보낼 때 "Content-Type 'application/octet-stream' is not supported" 해결

    예상치 못한 오류 발생: Content-Type 'application/octet-stream' is not supported

    userCreateRequest를 Text로 잘 지정했음에도 문제가 발생했고, 500 error가 떠서 이건 PostMan의 설정 문제라고 판단

    postman에서 form-data로 JSON 문자열을 보낼 때 Content-Type이 application/json으로 지정되지 않아서 발생한 듯

    그래서 Content-Type에 application/json을 명시적으로 작성해서 문제 해결

    개념 학습

    1. HttpOnly 쿠키

    • 자바스크립트(JS)에서 접근 불가능한 쿠키
    • XSS(스크립트 삽입 공격)으로부터 보호하기 위해 만들어진 옵션
    • HttpOnly=false 쿠키: 자바스크립트에서도 읽을 수 있는 쿠키
      • 보안은 다소 약해지지만, FE가 이 값에 직접 접근해서 뭔가 해야 할 때 필요

    2. CsrfTokenRequestHandler

    • Spring Security에서 **CSRF(크로스 사이트 요청 위조) 공격을 방지하기 위해 사용되는 인터페이스(**CSRF 토큰을 읽고 검증할 때 사용)
    • 기본적으로 CsrfTokenRequestAttributeHandler 구현체 사용
    • 역할
      • 토큰 검증: 클라이언트 요청에 포함된 CSRF 토큰이 유효한지 검증하는 역할
      • 토큰 생성 및 제공: CSRF 보호가 필요한 요청에서 토큰이 없는 경우, 새로운 CSRF 토큰을 생성하고 이를 요청 속성(request.setAttribute)으로 저장하여 응답 시 프론트엔드로 전달
      • 프론트엔드 연동: JavaScript 프레임워크 등은 이 속성에 저장된 CSRF 토큰을 사용하여 HTTP 요청 헤더에 토큰을 포함시켜 서버로 전송
    • Spring 공식문서에서 권장하는 CSR+SPA(Single Page Application) 환경에 적합한 구현체 커스텀마이징 가능
    public class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
        private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
        private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
            /*
             * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
             * the CsrfToken when it is rendered in the response body.
             */
            this.xor.handle(request, response, csrfToken);
            /*
             * Render the token value to a cookie by causing the deferred token to be loaded.
             */
            csrfToken.get();
        }
    
        @Override
        public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
            String headerValue = request.getHeader(csrfToken.getHeaderName());
            /*
             * If the request contains a request header, use CsrfTokenRequestAttributeHandler
             * to resolve the CsrfToken. This applies when a single-page application includes
             * the header value automatically, which was obtained via a cookie containing the
             * raw CsrfToken.
             *
             * In all other cases (e.g. if the request contains a request parameter), use
             * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
             * when a server-side rendered form includes the _csrf request parameter as a
             * hidden input.
             */
            return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
        }
    }
    • SecurityFilterChain 등록 방법
    http.csrf(csrf -> csrf
            ...
            .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
    );

     

    728x90