CSRF 보호와 CORS를 둘다 사용하는 이유는 무엇인가요?

1 week ago 3

  • Cross-Site Request를 고민하다 보니 CSRF 보호와 CORS가 둘 다 필요하다는 것이 처음에는 이해되지 않았음. 하지만 이를 설명하려면 많은 단어가 필요함

CSRF와 CORS

  • CSRF (Cross-Site Request Forgery)
    • 과거에는 흔했지만, 현재는 대부분의 웹 프레임워크에서 기본적으로 보호 기능을 제공하여 거의 문제가 되지 않음
    • 공격 방식: 사용자가 악성 사이트에서 특정 폼을 클릭하게 만들어 크로스 사이트 요청을 전송하도록 유도함
    • 방어 방식: 요청이 타 사이트에서 유입된 것이 아닌지 확인하는 것
  • CORS (Cross-Origin Resource Sharing)
    • HTTP 스펙의 일부로, 특정 크로스 사이트 요청을 허용하는 방법을 정의함
    • 사전 요청(preflight) 및 응답 헤더를 사용하여 어떤 출처(origin)에서 요청을 보낼 수 있는지 지정함

그렇다면 크로스 사이트 요청이 기본적으로 허용되며 CSRF 보호가 필요한 것인지, 아니면 기본적으로 차단되며 CORS가 필요하여 허용하는 것인지? 정답은 둘 다임.

기본 동작 방식

  • 동일 출처 정책(Same-origin policy)
    • 브라우저가 강제하는 보안 정책
    • 일반적으로 크로스 사이트 쓰기(Write)는 허용, 읽기(Read)는 금지
    • 예를 들어, 브라우저는 폼을 통한 POST 요청은 허용하지만, 응답을 읽을 수는 없음
  • SameSite 쿠키 정책
    • 2019년에 쿠키의 기본 동작 방식이 변경됨
    • 기존에는 크로스 사이트 요청에서도 쿠키가 항상 전송되었음
    • 새로운 SameSite 속성이 추가되었으며, 기본값이 Lax로 변경됨
    • 2025년 기준, 96%의 브라우저가 SameSite 속성을 지원, 75%가 새로운 기본값(Lax)을 지원
    • 그러나 Safari는 이를 기본값으로 적용하지 않았으며, UCBrowser는 여전히 지원하지 않음
  • 사이트(Site)와 출처(Origin)의 차이
    • 출처(Origin): 프로토콜 + 호스트명 + 포트 조합
    • 사이트(Site): 프로토콜 + 최상위 도메인 + 1 조합 (서브도메인과 포트는 무시됨)

CORS

  • CORS는 동일 출처 정책을 특정 출처(origin)에 대해 예외적으로 허용하는 방식
  • 브라우저는 요청을 보내기 전에 OPTIONS 타입의 **사전 요청(preflight request)**을 전송함
  • 서버는 응답 헤더를 통해 허용 규칙을 정의 (Access-Control-* 헤더 사용)
  • CORS가 적용되는 요청 유형:
    • fetch 및 XMLHttpRequest
    • 웹 폰트
    • WebGL 텍스처
    • canvas에서 drawImage로 그린 이미지/비디오 프레임
    • CSS shape-outside 속성에서 사용하는 이미지
  • 단, 폼 제출은 예외적으로 CORS가 적용되지 않음
    • HTML 4.0의 <form> 태그는 오래전부터 크로스 사이트 요청을 허용하고 있었음
    • 따라서 기존 서버들은 이미 CSRF 공격을 방어하도록 설계되었어야 함
    • 서버는 응답을 공유하려면 Access-Control-Allow-Origin을 설정해야 하지만, 요청 자체는 사전 요청 없이도 수락됨

질문: SameSite 정책과 이 방식은 어떻게 일관성을 유지하는가?

CSRF 보호 방법

  • 크로스 사이트 쓰기 요청은 허용되지만 응답은 공유되지 않음
    • 대부분의 웹사이트에서는 크로스 사이트 쓰기를 허용하고 싶지 않음
  • 표준적인 CSRF 방어 방법
    • 사용자별 CSRF 토큰을 요청에 포함하여 검증
    • 방법:
      • 폼 제출: 숨은 입력 필드(hidden input)로 토큰 추가
      • JS 요청: 쿠키 또는 meta 태그에 저장한 후, 요청 헤더나 파라미터에 포함
  • JS 요청은 원래 크로스 사이트가 기본적으로 차단됨
    • 하지만 동일 사이트 요청(same-site request)에는 허용됨
    • CSRF 토큰을 포함하면 모든 요청에서 동일한 방식으로 검증 가능
  • 추가적인 보안 이점
    • 브라우저가 기본적으로 응답 읽기를 차단해야 한다는 가정에서 작동
    • Origin 헤더를 검사하는 것보다 더 보안성이 높음

질문: 일부 프레임워크에서는 CSRF 토큰을 주기적으로 변경함. 그 이유는?

브라우저의 역할

  • 웹 보안의 핵심은 브라우저가 신뢰할 수 있는지에 달려 있음
  • 브라우저는:
    • 동일 출처 정책을 강제
    • 응답이 허용되지 않으면 읽지 않도록 차단
    • SameSite=Lax 기본값을 적용할지 결정
    • CORS를 구현하고, 안전한 사전 요청을 보냄

우리는 사용 중인 브라우저를 신뢰해야 함.

결론

  • SameSite=Lax가 100% 브라우저에서 지원되면 보안이 더 강화되겠지만,
    현재는 여전히 크로스 사이트 POST 요청만 예외적으로 허용되는 상황
  • 따라서 개발자는 CSRF 보호를 지속적으로 고려해야 함

"인터넷이 점점 더 안전해지지만, 그만큼 과거와의 호환성도 점점 줄어들고 있음."

출처

  1. Same-origin policy
  2. caniuse SameSite cookie attribute
  3. OWASP CSRF cheatsheet
  4. CORS wiki with requirements
  5. CORS spec
  6. CORS on MDN
  7. Preflight request
  8. Origin request header
  9. Origin and Site

Read Entire Article