Node

express-session 쿠키 설정이 안되는 이슈

myeongil 2023. 10. 22. 21:16

최근 express와 함께 express-session을 사용하다가 생긴 쿠키가 발행되지 않는 이슈를 정리해보려고 합니다.


로컬 환경에선 express-session을 통해 발급된 쿠키를 Set-Cookie 헤더에 들어가며 잘 설정되고 있었지만, 개발환경에 배포하는 경우에 쿠키가 제대로 설정되지 않았습니다.

 

로컬과 개발 서버에서는 secure 설정과 도메인, same-site 정도의 차이가 있었습니다. 하지만 이런 경우, 대부분 쿠키가 헤더에 설정은 되지만 브라우저가 쿠키를 무시하거나 보내지 않는 문제였습니다.

 

하지만 이번 경우에는 Set-Cookie 헤더가 설정되지 않는 문제였습니다.

 

express-session 코드를 살펴보면 아래와 같은 부분을 확인할 수 있었습니다.

 

// express-session index.js
onHeaders(res, function(){
  if (!req.session) {
    debug('no session');
    return;
  }


  if (!shouldSetCookie(req)) {
    return;
  }


  // only send secure cookies via https
  if (req.session.cookie.secure && !issecure(req, trustProxy)) {
    debug('not secured');
    return;
  }


  if (!touched) {
    // touch session
    req.session.touch()
    touched = true
  }


  // set cookie
  setcookie(res, name, req.sessionID, secrets[0], req.session.cookie.data);
});

 

 

Set-Cookie 헤더를 추가하기 전 secure 쿠키인 경우 요청이 secure 한 요청인지 체크하는 로직이 존재합니다. 실제로 이 부분에서 express-session이 secure 한 요청이라고 판단해 쿠키를 설정하지 않아 발생한 문제였습니다.

 

if (req.session.cookie.secure && !issecure(req, trustProxy)) {
  debug('not secured');
  return;
}

 

그럼 어떤 경우에 요청이 secure 하다고 판단할까요?

내부 구현은 아래와 같습니다. req는 express의 request, trustProxy는 express-session의 prxoy 옵션을 의미합니다.

 

function issecure(req, trustProxy) {
  // socket is https server
  if (req.connection && req.connection.encrypted) {
    return true;
  }

  // do not trust proxy
  if (trustProxy === false) {
    return false;
  }

  // no explicit trust; try req.secure from express
  if (trustProxy !== true) {
    return req.secure === true
  }

  // read the proto from x-forwarded-proto header
  var header = req.headers['x-forwarded-proto'] || '';
  var index = header.indexOf(',');
  var proto = index !== -1
    ? header.substr(0, index).toLowerCase().trim()
    : header.toLowerCase().trim()

  return proto === 'https';
}

 

1. 커넥션이 암호화 되어있는가?

express 서버가 https인지를 의미합니다.

 

2. express-session의 proxy 옵션이 false 인지

express 서버 자체가 https가 아니고 명시적으로 proxy가 없다고 지정해 주었기 때문에 더 이상 확인할 필요 없이 secure 하지 않다고 판단합니다.

 

3. express-session의 proxy 옵션이 설정되어있지 않다면 req.secure가 true인가

req.secure는 req.protocol === 'https'와 같다고 공식문서에서 이야기하고 있는데요. req.protocol은 express의 trust proxy 옵션에 따라 x-forwarded-proto 헤더를 기준으로 설정되기도 합니다.

 

4. express-session의 proxy 옵션이 true로 설정되어 있다면 x-forwarded-proto 헤더가 https 인가

명시적으로 express-sesison proxy 옵션이 true로 설정되어 있다면 x-forwarded-proto 헤더를 기준으로 secure 한 지 확인합니다.

 

정리

express-session의 proxy 옵션이

 

1. true 이면

x-forwarded-proto 헤더를 사용하겠다.

 

2. false 이면

실제 커넥션 자체가 TLS/SSL이 아니라면 secure 하다고 판단하지 않겠다.

 

3. undefined 이면

express의 설정에 의존하겠다. 따라서 proxy가 존재한다면 반드시 express의 trust proxy를 활성화해주어야 합니다.


X-Forwarded-*

그렇다면 X-Forwarded-Proto라는 값이 무엇이길래 express도 express-session도 해당 헤더를 사용하는 것일까요?

 

여러 proxy(ex. 로드밸런서)들을 거쳐 서버에 도달하게 되면 서버는 가장 마지막 proxy에 대한 정보만 받게 됩니다. 즉 마지막 proxy에서 http로 보냈는지 https로 보냈는지, proxy의 ip는 무엇인지 host는 무엇인지 와 같은 정보만 받게 됩니다. 하지만 서버 입장에서는 proxy의 정보뿐만 아니라 가장 처음 요청을 보낸 클라이언트의 정보들도 필요합니다. 이런 경우에, 사용하는 헤더가 X-Forwarded- 로 시작하는 헤더들입니다.

 

X-Forwarded-Proto

클라이언트와 proxy 간 통신에 사용한 프로토콜을 담고 있습니다.

 

X-Forwarded-Host

클라이언트가 proxy에 요청한 원래 host 헤더를 의미합니다.

 

X-Forwarded-For

클라이언트와 클라이언트에서 서버에 도달할 때까지 거친 proxy들의 ip 주소를 담고 있습니다.