Spring/Spring Security

[Spring Security] CSRF & CORS

꾸준함. 2024. 10. 8. 17:40

CSRF (Cross-Site Request Forgery)

  • 웹 애플리케이션의 취약점을 악용해 사용자의 권한을 도용하여 악의적인 요청을 보내는 공격을 방지하기 위한 보안 기능
  • Spring Security는 CSRF 공격을 방지하는 기본적인 메커니즘을 제공하며 CSRF 보호는 Spring Security를 사용하는 대부분의 애플리케이션에서 기본적으로 활성화되어 있음

 

 

CSRF 공격

  • 사용자가 자신도 모르게 공격자의 의도된 작업을 수행하게 만드는 공격 기법
  • 사용자의 의지와는 무관하게 공격자가 의도한 행동을 수행해서 웹 페이지의 보안을 취약하게 하거나 수정/삭제 등의 작업을 하는 공격 방법
  • ex) Man in the Middle 공격을 통해 인증된 세션을 탈취하고 해당 세션을 기반으로 서버에 악성 요청을 보냄

 

Spring Security에서의 CSRF 보호

  • 스프링 시큐리티는 CSRF 공격을 방지하기 위해 사용자가 신뢰할 수 있는 요청을 구분하는 메커니즘 사용
  • 요청의 정당성을 검증하기 위해 CSRF 토큰 사용
    • 서버는 클라이언트에게 CSRF 토큰을 발급하고
    • 클라이언트는 해당 토큰을 함께 전송함으로써 서버에 자신의 요청이 신뢰할 수 있는 요청임을 증명

 

1. CSRF 보호가 작동하는 방식

  • 토큰 발급: 사용자가 GET 요청으로 웹 애플리케이션에 처음 접근할 때 서버는 클라이언트에게 CSRF 토큰을 발급하고 해당 토큰은 보통 HTML의 hidden 필드나 HTTP 헤더에 포함됨
  • 토큰 전송: 클라이언트는 POST, PUT, DELETE 같은 상태를 변경하는 요청을 보낼 때 CSRF 토큰을 함께 전송하고 서버는 해당 토큰을 검증하여 요청이 정당한지 검증
  • 서버 검증: 서버는 클라이언트로부터 받은 토큰과 서버에 저장된 토큰을 비교하여 요청이 위조되지 않았는지 검증하고 토큰이 일치하지 않을 경우 요청 거부

 

https://velog.io/@jeongbeom4693/Security-CSRF

 

2. CsrfFilter

  • 스프링 시큐리티에서 제공하는 CsrfFilter에서 CSRF 보호를 수행함
  • CsrfFilter는 하나의 클래스로 OncePerRequestFilter를 확장
  • RequestMatcher, CsrfTokenRequestHandler 등이 포함되어 있는 것을 확인할 수 있음
  • 해당 필터는 요청을 가로채서 GET, HEAD, TRACE, OPTIONS의 HTTP 요청은 모두 허용하지만 그 외 HTTP 요청은 토큰이 포함된 헤더가 있는지 확인하는 과정을 거침
    • 만약 헤더에 올바른 토큰 값이 없거나, 아예 헤더가 없는 경우 403 Forbidden으로 응답을 내려줌
    • 토큰은 하나의 문자열 값으로 GET, HEAD, TRACE, OPTIONS 요청 이외의 요청에 대해서는 헤더에 해당 토큰을 포함해서 요청해야 함

 

 

  • CsrfTokenRepository를 이용하여 새로운 토큰을 생성 및 저장하는 등 CSRF 토큰 값을 관리
    • 토큰은 UUID 값으로 생성
    • 토큰을 HTTP 세션에 저장

 

 

3. 코드 예시

  • 모든 요청이 인증을 요구하도록 설정
  • 로그인을 성공하면 루트 페이지로 리다이렉트되도록 설정
  • form 내 hidden input을 추가하여 POST 요청에 CSRF 토큰을 같이 전달하도록 설정
    • 로그인에 성공하면 hidden input의 value에 CSRF 토큰 값이 설정됨

 

3.1 SecurityConfig

 

 

3.2 html

 

 

 

3.3 특정 경로에만 CSRF 보호를 적용하고 싶을 경우

  • csrf()의 ignoringRequestMatchers를 활용하여 보호를 제외하고 싶은 경로 입력
    • /api/** 경로의 POST api에 대해서는 200 OK
    • 그 외 경로의 POST api에 대해서는 403 Forbidden

 

 

CORS (Cross-Origin Resource Sharing)

  • 웹 애플리케이션이 다른 도메인에서의 요청을 허용하는 방식과 관련된 보안 설정
  • 기본적으로 웹 브라우저는 보안 정책인 동일 출처 정책(Same-Origin Policy)에 따라, 서로 다른 도메인 간의 리소스 요청을 제한함
    • 그러나 특정 상황에서는 다른 도메인에서 리소스에 접근해야 할 때가 있으며, 이를 위해 CORS가 사용
    • ex) 백엔드와 프론트엔드가 별도의 애플리케이션으로 개발이 되는 경우에 이를 적용 (SpringBoot, React)

 

https://livebook.manning.com/book/spring-security-in-action-second-edition/chapter-10

 

1. CORS 동작 방식

  • CORS는 클라이언트와 서버 간의 상호작용을 제어하는 일련의 HTTP 헤더로 구성됨
  • 웹 애플리케이션이 다른 도메인으로부터 리소스를 요청할 때 서버는 브라우저가 해당 요청을 수용할지 여부를 결정할 수 있는 정보 제공
  • CORS는 기본적으로 두 가지 종류의 요청을 다룸
    • Simple Request
    • Preflight Request

 

1.1 Simple Request

  • GET, HEAD, POST 중 하나의 메서드로 수행되는 요청
  • POST 요청일 경우 `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain` 등의 Content-Type 헤더만 사용 가능
  • Simple Request는 바로 서버로 전송되며 서버는 응답의 헤더에 `Access-Control-Allow-Origin` 등을 포함하여 해당 요청의 허용 여부를 브라우저에 알림

 

1.2 Preflight Request

  • PUT, DELETE 같은 메서드, 커스텀 헤더가 포함된 요청, `application/json` 등 특별한 Content-Type이 있을 경우에는 브라우저가 실제 요청을 보내기 전 Preflight Request를 보냄
  • Preflight Request는 OPTIONS 메서드로 서버에 보내지며 서버는 이에 응답해 해당 요청의 허용 여부를 브라우저에 알려줌

 

 

2. CORS 관련 헤더

  • HTTP 헤더를 기반으로 작동하며 중요한 헤더로는 다음의 3개의 헤더가 존재함
    • Access-Control-Allow-Origin: 서버가 해당 요청을 허용할 수 있는 출처(origin)를 명시, 브라우저는 해당 값이 클라이언트의 출처와 일치할 때만 요청을 허용
    • Access-Control-Allow-Methods: 서버가 허용하는 HTTP 메서드를 지정 ex) GET, POST, PUT, DELETE
    • Access-Control-Allow-Headers: 클라이언트가 보내는 요청에 포함될 수 있는 헤더들을 명시

 

* 주의: Spring Security는 이러한 헤더를 응답에 기본적으로 추가하지 않기 때문에 별도로 추가해줘야 함

 

3. 코드 예시

  • CORS 관련 테스트에 집중하기 위해 CSRF는 비활성화하며 모든 요청을 허용하도록 처리
  • 브라우저에서는 localhost:8080으로 접근하고 javascript에서는 127.0.0.1:8080으로 api를 찔러 응답 값을 화면에 노출하도록 설정
  • SecurityConfig에서 별도로 setAllowedOrigins, setAllowedMethods 설정 유무에 따른 차이를 확인 가능

 

3.1 SecurityConfig

  • CORS 설정 확인을 위해 CorsConfigurer을 통해 setAllowedOrigins, setAllowedMethods 설정한 부분을 주석 처리


 

3.2 html


 

 

부연 설명

  • Access-Control-Allow-Origin HTTP 헤더를 설정하는 부분이 주석처리 되어있기 때문에 응답이 수락되지 않음
    • 별도의 설정이 없을 경우 SpringBoot는 CORS 헤더를 설정하지 않음
    • 교차 출처 호출을 막아두기 때문에 브라우저에서는 아무것도 노출이 안 되는 것이 맞음

 

  • 주석을 해체할 경우 정상적으로 동작하는 것을 확인 가능

 

3.3 CorsConfigurer 외 CORS 허용하는 방법

  • @CrossOrigin 어노테이션을 활용하여 여러 출처를 정의하는 배열을 입력 가능
  • 모든 출처를 허용하도록 *를 활용할 수도 있지만 이렇게 설정할 경우 XSS(교차 사이트 스크립팅) 요청에 노출되어 보안 취약점들이 많아짐
  • 엔드 포인트를 정의하는 곳에서 @CrossOrigin을 활용하면 명시적이긴 하지만
    • 동일한 코드가 반복되고
    • 코드 복잡성이 올라가고
    • 개발자가 새로 구현하는 엔드포인트에 대해 매번 적용해줘야하는 문제점 발생

 

  • 따라서 CorsConfigurer을 통해 CORS를 허용하는 것을 권장

 

 

참고

https://fastcampus.co.kr/classroom/240071

반응형