-
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 보호를 지속적으로 고려해야 함
"인터넷이 점점 더 안전해지지만, 그만큼 과거와의 호환성도 점점 줄어들고 있음."
출처
-
Same-origin policy
-
caniuse SameSite cookie attribute
-
OWASP CSRF cheatsheet
-
CORS wiki with requirements
-
CORS spec
-
CORS on MDN
-
Preflight request
-
Origin request header
-
Origin and Site