cross-origin 환경에서 쿠키 전송이 실패하는 이유
웹에서 쿠키 기반 인증을 구현하다 보면, 분명 서버에서는 쿠키를 내려주고 있는데 클라이언트 요청에는 쿠키가 포함되지 않는 상황을 종종 마주하게 됩니다.
이 문서는 이러한 문제를 겪으면서 정리한 내용으로, same-site, same-origin, withCredentials 개념을 쿠키 전송 관점에서 정리해보겠습니다.
1. Same-Site 란 무엇인가
1.1 정의
Same-Site는 현재 요청을 보내는 origin을 기준으로, 해당 요청이 같은 사이트(site) 에서 발생했는지를 판단하기 위한 개념입니다.
여기서 site는 Schemeful Same-Site 기준에 따라 다음 요소로 판단합니다.
scheme + registrable domain
기준 Origin 예시
기준 Origin: https://example.com
| URL | site |
|---|---|
| https://api.example.com | example.com |
| https://www.example.com | example.com |
| http://example.com | ❌ 다름 (scheme 다름) |
| https://example.co.kr | ❌ 다름 |
즉,
- 서브도메인이 달라도 same-site로 판단되며
- scheme(http/https)가 다르면 cross-site로 판단됩니다.
1.2 Same-Site 예시
same-site 요청
FE: https://app.example.com
API: https://api.example.com
- registrable domain이
example.com으로 동일하고 - scheme이 https로 동일하므로, same-site 요청으로 판단됩니다.
cross-site 요청
FE: https://app.example.com
API: https://api.other.com
- registrable domain이 다르므로, cross-site 요청으로 판단됩니다.
1.3 SameSite 쿠키 속성
쿠키는 SameSite 속성을 통해 언제 요청에 포함될지를 제어합니다.
| SameSite 값 | 동작 |
|---|---|
| Strict | 같은 site + 같은 컨텍스트에서만 전송 |
| Lax | same-site + top-level navigation |
| None | cross-site 요청에도 전송 가능 (단, Secure 필수) |
2. Same-Origin이란 무엇인가
2.1 정의
Same-Origin은 현재 문서 또는 스크립트가 로드된 origin을 기준으로, 해당 요청 또는 리소스가 같은 origin에서 발생했는지를 판단하는 개념입니다.
여기서 origin은 다음 3가지 요소의 조합으로 구성됩니다.
scheme + host + port
2.2 Same-Origin 예시
| URL A | URL B | 결과 |
|---|---|---|
| https://example.com | https://example.com | same-origin |
| https://example.com | https://api.example.com | ❌ |
| https://example.com | http://example.com | ❌ |
| https://example.com | https://example.com:8080 | ❌ |
세 요소 중 하나라도 다르면 다른 origin으로 판단됩니다.
2.3 Same-Origin Policy
브라우저는 기본적으로 다른 origin 간 리소스 접근을 제한합니다. 이를 Same-Origin Policy(SOP) 라고 합니다.
차단되는 대표적인 예시는 다음과 같습니다.
- JS에서 다른 origin의 응답 body 접근
- 쿠키 자동 전송
- localStorage 접근
이 제한을 완화하기 위해 CORS 설정을 사용하게 됩니다.
3. Cookie와 Same-Site / Same-Origin의 관계
3.1 핵심 정리
| 개념 | 쿠키와의 관계 |
|---|---|
| Same-Site | 쿠키의 전송 범위를 결정 (CSRF 방어). 요청하는 사이트와 목적지 도메인이 같은지 확인하여 쿠키를 보낼지 결정합니다. |
| Same-Origin | 브라우저의 보안 경계를 결정. 다른 출처(Origin)의 스크립트가 데이터에 접근하는 것을 막는 '동일 출처 정책(SOP)'의 기준입니다. |
| CORS | Cross-Origin 요청 허용 여부를 결정. SOP로 막힌 요청을 서버의 허락하에 안전하게 열어주는 메커니즘입니다. |
| withCredentials | 클라이언트(JS)의 의지. Cross-Origin 요청 시 "내가 가진 쿠키를 실어서 보낼게"라고 설정하는 옵션입니다. |
3.2 다른 origin에서 쿠키를 사용하려면
다른 origin 간 쿠키를 사용하기 위해서는 다음 조건들이 모두 충족되어야 합니다.
단, 다른 origin이라 하더라도 same-site인 경우에는
SameSite=Lax설정으로도 쿠키 전송이 가능합니다.본 문서에서는 cross-site 환경까지 포함한 일반적인 조건을 설명하기 위해
SameSite=None을 기준으로 설명합니다.
쿠키 설정
Set-Cookie: accessToken=xxx;
Path=/;
SameSite=None;
Secure;
HttpOnly;
SameSite=None으로 설정하여 cross-site 요청에서도 쿠키가 전송되도록 합니다.SameSite=None쿠키는 브라우저 정책에 따라Secure설정이 필수이므로, HTTPS 환경에서만 쿠키가 전송되도록 제한됩니다.HttpOnly를 설정하여 JavaScript에서 쿠키에 접근할 수 없도록 합니다.
2) 서버 CORS 설정
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
3) 클라이언트 withCredentials 설정
클라이언트에서는 cross-origin 요청 시 쿠키를 포함해서 전송할지 여부를 명시적으로 설정해주어야 합니다.
브라우저의 기본 동작에 따르면,
- same-origin 요청의 경우 쿠키가 자동으로 포함되지만
- cross-origin 요청의 경우,
withCredentials: true로 설정하지 않으면 브라우저가 쿠키를 요청에 포함하지 않습니다.
따라서 cross-origin 환경에서 쿠키 기반 인증을 사용하려면 클라이언트에서 반드시 withCredentials: true 설정이 필요합니다.
Axios: axios.defaults.withCredentials = true;
Fetch API: fetch(url, { credentials: 'include' });
4. 결론
1. 브라우저에서 cross-origin 요청 발생
2. JS에서 withCredentials: true 설정
3. 서버에서
- Access-Control-Allow-Origin 명시
- Access-Control-Allow-Credentials: true 설정
- SameSite=None; Secure 쿠키 설정
4. 결과적으로 쿠키가 포함된 요청이 정상 처리됩니다
'네트워크' 카테고리의 다른 글
| TCP (1) | 2023.11.21 |
|---|---|
| RDT(Reliable Data Transfer) (1) | 2023.11.21 |
| Multiplexing, deMultiplexing (0) | 2023.10.25 |
| 소켓 (0) | 2023.10.23 |
| DNS (0) | 2023.10.19 |