사용자 인증 기능을 개발하다가 CORS와 coockie 때문에 고생했던 경험이 있었다.
이에 대한 정의와 목적은 MDN이나 web.dev에 잘 설명되어 있으니 여기서는 간단한 개념 정리와 예시(백엔드 express.js와 프론트엔드에서 구현)와 세션쿠키를 사용할 때 주의점에 대해서 정리해보려 한다.
먼저 아래의 질문을 모르겠다면 공식 문서를 다시 읽어보면 좋을 것 같다.
브라우저의 접속 주소(https://192.168.100.4:3000), 웹 서버 주소(http://192.168.100.4:4000) 인 경우
same-origin / cross-ogirin
same-site / cross-site
각각 무엇일까?
답:
cross-origin, same-site
문제가 됐던 경우는 위와 같은 환경에서 쿠키를 사용하여 사용자 인증을 하는 경우, 쿠키의 sameSite 속성이 lax, strict 여도 쿠키 전달이 잘 될 것이다.
그러나 배포하는 과정에서 cross-stie가 되어버리면, 쿠키전달이 안될 것이다.
예를 들어, vercel에 프론트엔드를, aws에 백엔드를 배포하는 경우다. (별다른 설정을 하지 않았을 경우)
이런 경우 sameSite를 none으로 설정하거나
프론트엔드와 백엔드 앞단에 프록시 서버를 띄우거나
등등 해결할 수 있는 방법은 많지만 crossSite라서 쿠키 전달이 안되는 걸 까먹지말자
CORS(Cross-Origin Resource Sharing)
먼저 origin은 url에서 아래 그림과 같다.

아래 표를 보고 same-origin과 cross-origin을 헷갈리지 말자

CORS는 브라우저가 자신의 origin이 아닌 다른 origin에서 자원(resource)를 서버가 로딩하는 것을 허용하도록 서버가 허가해주는 HTTP 헤더 기반 매커니즘이라고 한다는데 참 어려운 문장이다.
간단하게 말하면 domain-a.com(브라우저)에서 domain-b.com(백엔드)에 HTTP 요청을 할 수 없다.
예시를 보면 이해하기 쉬울 것 같다.
아래 그림을 보면 웹 페이지는 domain-a.com에서 호스팅 되었고, image는 domain-a.com (same-origin)에 요청하고 Canvas관련 데이터는 domain-b.com(cross-origin)에 요청한다.
만약 CORS 설정이 안되어있다면 Canvas 관련 데이터를 가져오지 못할 것이다.

CORS를 피하기 위해서는 아래와 같은 두 가지 방법이 있는데 1번에 대해서만 작성해보려 한다.
1. 백엔드에서 Access-Control-Allow-Ogirin 헤더 설정
2.브라우저에서 접속할 프록시 서버 사용
프록시 서버를 경유할 경우 브라우저에서는 CORS를 피할수 있고, 프록시 서버의 경우는 브라우저가 아니므로 CORS 정책에 위배되지 않음 ( CORS는 브라우저 정책)
https://create-react-app.dev/docs/proxying-api-requests-in-development/
(react로 개발할 때 CORS 문제가 생기면 위 문서를 따라하면 되는 것으로 보임)
1번의 경우 아래의 케이스로 나뉠 것 같음
case #1) 모든 cross-origin에서 HTTP 요청이 가능하게 할 경우
case #2) 특정 cross-origin에서만 요청이 가능하게 할 경우
case #3) HTTP 요청에 사용자 인증이 정보가 포함되어 있는 경우
구현
백엔드
case #1) 모든 cross-origin에서 HTTP 요청이 가능하게 할 경우
Access-Control-Allow-Origin 헤더를 와일드카드(*)로 설정
Access-Control-Allow-Origin: * (모든 도메인에서의 요청을 허용)
case #2) 특정 cross-origin에서만 요청이 가능하게 할 경우
Access-Control-Allow-Origin 헤더에 특정 origin을 추가
Access-Control-Allow-Origin: https://www.example.com
case #3) HTTP 요청에 사용자 인증이 정보가 포함되어 있는 경우
Access-Control-Allow-Credentials 헤더를 true로 설정
Access-Control-Allow-Origin 헤더에 특정 origin을 추가 (Access-Control-Allow-Credentials가 true일 경우 * 사용 불가)
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true (쿠키, Authorization 헤더 등 인증정보 포함 여부)
백엔드에서 express를 사용한 구현 예시 (cors모듈 사용)
case #1
case #2
case #3
프론트엔드
#1, #2 케이스에서 프론트엔드가 할 일은 없고
#3 케이스에서처럼 HTTP 요청에 사용자 인증 정보를 포함해야 하는 경우 아래와 같이 fetch 옵션에 credentials: 'include' 추가
프론트엔드에서 구현 예시 (fetch)
CORS는 생각보다 생각할게 없는데, 사용자인증을 쿠키로 하는 경우에 조금 헷갈린다.
글을 적다보니 생각난건데, 구글 OAuth 사용할 때 승인된 Javascript 원본에 URI을 넣어야 했는데 OAuth 인증하는 서버에서 #3 케이스와 같은 CORS 설정이라 origin을 추가하기 위해 적었던 게 아닐까?

Cookie sameSite
스키마와 eTLD+1이 동일한 웹사이트는 '동일 사이트'로 간주됩니다. 스키마가 다르거나 eTLD+1이 다른 웹사이트는 '크로스 사이트'( 2019년 말부터 same-site에는 스키마(프로토콜)가 포함됨) 라고 하는데.. 뭔소린가 싶다.
아래 예시를 보면
TLD(Top-Level domain)은 .com이고 TLD+1은 example.com이다. 여기서 TLD+1과 scheme(프로토콜)이 같으면 same-site로 간주한다는 것이다.
(포트는 달라도 된다는 게 충격이다. 당연히 cross-site인줄 알았다.)

아래 표를 보고 cross-site 구분하면 될 것 같다.

쿠키의 same-site 속성
None
cross-site 에서도 쿠키 전달 가능하지만 쿠키의 secure 옵션을 true로 설정해야 함
ex)
https://domain-a.com (브라우저) -> https://domain-b.com (백엔드) (O)
http://domain-a.com (브라우저) -> http://domain-b.com (백엔드) (X)
Lax
cross-site 에서는 특정 방법으로만 쿠키 전달 가능
- Top Level Navigation, 안전한 HTTP 메소드 요청(HEAD, OPTIONS,..)
- (예를 들어 <a> 태그를 통한 이동, window.location.href을 사용하는 이동)
- <iframe>, <img> 태그 등에서는 쿠키를 전송하지 않음
- fetch와 XMLHttpRequest 요청에도 쿠키를 전달하지 않음
ex)
https://domain-a.com (브라우저)에서 사용자가 <a href="https://domain-b/session">를 클릭하여 이동할 경우
domain-b의 쿠키가 백엔드로(https://domain-b) 전달됨
예를 들어, 사용자가 구글 검색을 통해 domain-b.com에 접속하게 될 경우 domain-b의 쿠키가 백엔드로 전달됨
만약 domain-b의 session cookie가 lax라면, 로그인이 되어있음
Strict
cross-site 에서는 쿠키를 전달하지 않음
ex) 사용자가 구글 검색을 통해 domain-b.com에 접속하게 될 경우 domain-b의 쿠키가 백엔드로 전달되지 않음
만약 domain-b의 session cookie가 strict라면, 로그인이 되어있지 않을 것이라고 예상할 수 있지만 domain-b.com 접속 요청 요청에는 쿠키가 없어도, 이후 프론트엔드에서 로드된 js에서의 요청에는 session cookie가 포함될 것이므로 로그인이 되어있을 수 있음
구현
백엔드 (express)
res.cookie("example_cookie", "cookie_value", {
sameSite: "none", //lax or strict
secure: true, //lax or strict에서는 false도 가능
maxAge: 24 * 60 * 60 * 1000,
httpOnly: true //클라이언트단에서 js로 접근 불가능
});
프론드엔드
아래와 같이 fetch 옵션에 credentials: 'include' 추가
테스트

sameSite: "lax"일 때 아래와 같이 쿠키가 전달됨

sameSite: "strict"일 때 아래와 같이 쿠키가 전달되지 않음

sameSite: "strict"일 때, 웹 페이지 내의 js 코드에서 /api/session 요청으로 session 데이터를 받을 경우 아래와 같이 잘 나옴

이러면 대체 왜 lax와 strcit를 구분하는 걸까
cross-site에서의 첫 요청의 쿠키 전달 여부가 어떤 의미가 있는걸까
백엔드에서 정적 페이지를 만들어서 서빙하면 의미가 있을 것도 같은데 너무 옛날 방식이라..
--> 생각해보니 Next.js에서도 서버 사이드 렌더링을 하니 사용자 인증을 strict 쿠키로 하면 사용자 인증이 안되겠네
끝.
'Web > Web 전반' 카테고리의 다른 글
| [CSS] 유틸리티 클래스에 대하여 (0) | 2025.03.09 |
|---|---|
| express.js self signed SSL 인증서 적용 (0) | 2025.02.27 |
| open SSL을 이용한 self signed SSL 인증서 발급 (0) | 2025.02.27 |
| [날짜 관련] 트러블 슈팅 (1) | 2025.02.09 |
| DOM 용어에 대하여 (0) | 2025.01.05 |