본문 바로가기

TIL/Node.js, express

SameSite 정책으로 인해 브라우저로 쿠키가 전송되지 않는 문제 해결

배경

서버에서 클라이언트(브라우저)로 쿠키에 값을 담아 보내야 할 때, res.cookie(key, value)와 같은 형태로 보낸다.

 

그런데 이때, 쿠키를 주고 받는 서버와 클라이언트의 origin(출처)가 다르므로, 추가 설정을 하지 않는다면 CORS(Cross-Origin Resource Sharing) 에러가 뜬다.

 

더보기

CORS, origin(출처) 란?

CORS(Cross-Origin Resource Sharing)

말 그대로 서로 다른 origin 간에 리소스를 주고 받을 때와 관련한 보안 정책이다.

 

origin은 어디까지를 의미하나?

보통 origin이라 함은 프로토콜과 호스트, 그리고 포트 번호까지를 합친 값을 의미한다.

즉, 포트번호까지 같아야 cors에 해당하지 않는다.

 

이때 ‘쿠키를 첨부해서 보내는 http 요청’은 credential 정보가 포함되어 있는 요청이므로,

 

  • 클라이언트 측에선 해당 axios 요청에 withCredentials: true 설정을,
  • 서버 측에선 응답 헤더의 Access-Control-Allow-Credentials 항목과 Access-Control-Allow-Origin 항목을 각각 설정하였다.

 

+ nodejs에서 cors 관련 응답헤더 설정

// app.js
const cors = require("cors");
app.use(
  cors({
    origin: "http://localhost:3001",
    credentials: true,
  })
);

 


하지만 그럼에도 쿠키가 보내지지 않는다.

 

 

해결

이는 크롬 브라우저의 sameSite 보안 정책과 관련하여 발생하는 문제인데, 우선 쿠키에 대해 알아보자.

쿠키란?

쿠키란, 쉽게 말해 ‘브라우저에 저장되는 데이터’이다.

 

브라우저에서 서버로 요청을 보낼때, 그 요청에 대한 응답에 Set-Cookie 헤더가 포함되어 있는 경우, 브라우저는 Set-Cookie에 있는 데이터를 저장하고, 이 저장된 데이터를 쿠키라고 부른다.

 

Set-Cookie: color=orange
// node.js에서 res.cookie(key, value)

 

위와 같이 서버의 응답에 Set-Cookie 헤더가 포함된 경우, color라는 key 값에 orange라는 value가 쿠키로 저장된다. (쿠키는 key-value 형태의 데이터셋)

 

그리고 이렇게 저장된 쿠키는, 이후 브라우저에서 서버로 다시 요청을 보낼때마다 Cookie라는 헤더에 자동으로 담겨 같이 전송된다.

 

바로 이 지점 때문에 보안 문제가 발생할 수 있는데, 예컨대 html 상에 이미지나 a태그에 (처음 쿠키를 보낸 origin이 아닌) 제3의 origin(도메인)이 담겨 있을 경우 클라이언트는 해당 도메인으로도 쿠키를 전송하게 된다.

 

이를 서드 파티 쿠키라고 하는데, 공격자가 자신의 도메인으로 서드 파티 쿠키를 보내도록 유도해 이를 악용할 수 있다.

 

이를 CSRF(Cross Site Request Forgery) 공격이라고 하는데, 이를 방지하기 위한 정책이 바로 SameSite이다.

 

SameSite

SameSite는 앞서 말한 서드 파티 쿠키의 보안 문제를 해결하기 위해 고안된 정책(기술)이다.

 

SameSite 쿠키는 다음 세 가지 정책이 있다.

 

  • None: SameSite 탄생 이전 쿠키와 동작 방식이 같음. 크로스 사이트 요청의 경우에도 쿠키가 항상 전송됨.
  • Strict: 가장 보수적 정책. 크로스 사이트에는 항상 전송되지 않음.
  • Lax: 경우에 따라 전송됨

 

크롬 브라우저의 SameSite 정책

다른 브라우저들과 달리 크롬 브라우저는 SameSite의 default값이 None이 아닌 Lax이다.

 

바로 이러한 이유 때문에, 크롬을 사용하던 내 환경에서 쿠키가 전송되지 않았던 것.

 

SameSite: none으로 설정하면 해결된다.

 

Secure 필수 정책

단, 보안 문제 때문에 크롬에서는 SameSite를 None으로 사용하려면 반드시 Secure를 true로 설정해야 한다.

 

res.cookie("test", {
          secure: true,
          sameSite: "none",
          httpOnly: process.env.HTTP_ONLY,
        });

 

++

secure: true로 설정하기 위해서는 https 프로토콜을 사용해야 하는데, 다음 글들을 참고하여 개발 환경에 따라 https를 적용하면 된다.

 

<로컬 환경에서 https 적용 - mkcert 사용>

🔐 https로 React 로컬 테스팅하기

 

🔐 https로 React 로컬 테스팅하기

크롬 업데이트하고 로컬에서 로그인이 안 되네..?!

velog.io

 

<nginx, certbot 사용하여 https 배포>

2022.11.30 - [TIL/DevOps] - AWS EC2, Nginx를 사용하여 서비스 배포하기(with React.js, express.js) - 2

 

AWS EC2, Nginx를 사용하여 서비스 배포하기(with React.js, express.js) - 2

Nginx 사용하여 React 앱 배포, https 연결 (by certbot) Nginx로 React 배포하기 EC2에서 React App을 빌드한다 - 빌드 전에 반드시 git pull을 받고, .env 등의 설정 파일이 배포환경에 맞는지 확인한다. .env가 없으

devtofreedom.tistory.com

 

 

 

References

https://velog.io/@code-bebop/CORS%EC%9D%98-Cookie

 

CORS의 Cookie

서론 서버와 클라이언트의 통신을 이용한 로그인 기능을 구현하는 것에 쿠키를 사용하기로 했다. 왜냐하면 httpOnly 쿠키 헤더를 활성화하면 XSS 공격은 어느 정도 방어가 가능하고, 쿠키는 클라이

velog.io

https://evan-moon.github.io/2020/05/21/about-cors/

 

CORS는 왜 이렇게 우리를 힘들게 하는걸까?

이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

evan-moon.github.io

https://junglast.com/blog/http-ajax-withcredential

 

HTTP Ajax 요청시 사용하는 withCredentials 옵션의 의미

Ajax 요청에서 'credential'의 의미

junglast.com

https://seob.dev/posts/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%99%80-SameSite-%EC%86%8D%EC%84%B1/

 

브라우저 쿠키와 SameSite 속성 / seob.dev

브라우저 쿠키에 대한 기본적인 내용들, 그리고 웹 개발자들에게 중요한 "SameSite" 속성을 다룹니다. "SameSite" 속성이 어떤 속성인지, 각 브라우저에서 어떻게 동작하고 있는지 알아봅니다.

seob.dev