express-session을 쓸 때 가장 찜찜한 부분은 아무래도 secret에 무엇을 넣을지에 대한 고민일 것 같습니다. 많은 자료들이 그냥 공식 문서에 나와 있듯이 'keyboard cat'인 채로 놔두고 이유를 설명하지 않지만, 대충 그런 식으로 아무 문자열이나 넣어도 되는 것 같지는 않아보였습니다.
이러한 여러 의문점들을 블로그 글을 작성하면서 해소해보겠습니다.
express-session의 공식 레퍼런스를 참고했습니다.
공식문서 바로 가기
그런데 공식 문서가 너무 빈약해서, SO 등 다양한 자료를 찾아봐야 했습니다.
그러한 의문들에 대해 나름대로 스스로 답을 구해봤습니다.
secret은 session ID cookie를 서명하는 데에 사용됩니다. 이를 통해 쿠키 변조를 막을 수 있습니다.
⚠️ 주의: 쿠키나 세션을 암호화하는 것이 아닙니다! 많은 자료들에서 세션을 암호화한다고 모호하게 설명하고 넘어가는데, 이는 잘못된 사실이라고 합니다! 서명과 암호화는 다른 개념이라고 합니다. 서명은 내용 유출을 막을 수 없고, 오로지 무결성만을 보장한다고 합니다.
https://github.com/tj/node-cookie-signature
여기서 확인할 수 있습니다!
var cookie = require('cookie-signature');
var val = cookie.sign('hello', 'tobiiscool');
val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI');
var val = cookie.sign('hello', 'tobiiscool');
cookie.unsign(val, 'tobiiscool').should.equal('hello');
cookie.unsign(val, 'luna').should.be.false;
위 코드를 보시면 아실 수 있듯이, 쿠키 서명은 굉장히 직관적이고 간단한 개념이라서, hello
를 tobiiscool
로 서명하면 hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI
가 나옵니다. 그 값을 tobiiscool
로 unsign하면 'hello'가 나옵니다. 따라서 유저가 hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI
값을 보내오면 서버가 숨겨진 secret tobiiscool
값으로 서명을 해보고, 그 값이 . 앞의 값과 동일하면 쿠키가 변조되지 않았다는 사실을 확인할 수 있습니다.
공식 문서에서는 이러한 방식의 cookie signing이 cookie tampering을 막음으로써 session hijacking의 위험을 줄일 수 있다고 적혀 있습니다.
세션 탈취 공격은 공격자가 유저의 웹 세션을 가로채서 해당 사용자로서 시스템에 접근하는 방식의 공격입니다.
등이 있습니다.
쿠키 서명은 쿠키 데이터의 무결성을 보장합니다. 서버는 변조를 감지할 수 있습니다. 이러한 권한이 어떻게 세션 하이재킹을 방지할 수 있을까요? 직접적으로 방어할 수 있는 것은 아닐 듯 합니다.
https://www.reddit.com/r/webdev/comments/es5g5e/why_does_a_server_sign_a_cookie/
위 글에 따르면 다음 예시와 같은 방식으로 피해를 최소화 할 수 있다고 합니다.
200 OK
Set-Cookie: SessionID=7
이런 식으로 서버가 유저에게 응답하면
GET /MyAccountInfo
Set-Cookie: SessionID=7
유저는 다음에 서버에 위와 같은 http 요청을 보낼 수 있게 됩니다.
이때, 공격자가 나타나서 쿠키를 탈취하고, 내용을 참고하여 SessionID의 패턴을 예측하고, 새로운 쿠키를 만들어서 서버에 요청을 보내려고 시도합니다.
GET /MyAccountInfo
Set-Cookie: SessionID=8
서버는 꼼짝없이 위와 같은 공격에 당할 수 밖에 없습니다.
하지만 쿠키에 서명을 한다면 어떨까요?
200 OK
Set-Cookie: SessionID=7_8994e60788425e626e956576e4
이러면 공격자는 비록 SessionID가 7이라는 것을 알게 되어도, 쿠키의 내용을 변조할 수는 없어서 다른 유저에게 피해를 입힐 수 없게 됩니다.
이 방식의 한계는, 공격자가 여전히 SessionID가 7인 유저인 척 할 수는 있다는 점입니다. 그래서 완벽히 피해를 차단할 수는 없습니다.
하지만 쿠키 데이터가 전혀 변조되지 않았다는 사실을 안다는 것만으로도 얻을 수 있는 장점은 분명합니다!
secret은 랜덤하고 예측 불가능한 문자열이어야 합니다.
제 생각에는 아마 crypto 모듈을 사용해서 안전하게 생성할 수 있을 것 같습니다.
⚠️ 공식 문서에 이에 대해 세부적으로 적혀있지는 않습니다.
안전성을 보장하기 위해 충분한 길이가 필요하지만, 또 너무 길면 성능에도 영향을 줄 수 있고 그렇게 큰 도움이 되지는 않을 것 같습니다.
저와 비슷한 고민을 한 분들이 있습니다!
https://stackoverflow.com/questions/19732458/expressjs-ideal-session-secret-size
SO의 댓글에 따르면, express-session은 sha256을 서명에 사용하므로, 이를 위해 권장되는 최소의 키 크기인 256bit, 즉 32바이트의 길이면 충분하다고 하면서, 크립토 SO 답변을 근거로 들었습니다.
const crypto = require('crypto');
const secret = crypto.randomBytes(32).toString('hex');
위 코드를 통해 생성해서 사용하면 되겠네요! 물론 딱 한번만 실행하고, 해당 값이 git 같은 곳에 올라가지 않도록 환경 변수로 따로 빼서 process.env.SESSION_SECRET
로 사용해야 합니다.
secret이 변경되면 기존의 secret으로 서명된 세션 쿠키는 무효화됩니다. 따라서 사용자들은 로그아웃 상태가 될 수 있으며, 그들은 다시 로그인 해야 합니다. 이런 이유로 secret을 교체하는 것은 사용자 경험에 영향을 줄 수 있기 때문에, 교체 작업 시 주의가 필요합니다.
잘 모르겠습니다.
세션 데이터의 민감도에 따라 다르겠지만, secret을 바꾸면 그 이전 세션들이 모두 invalidate되니까, 너무 잦아도 안될 것 같아요!
https://github.com/expressjs/session/issues/591
이러한 재밌는 이슈 기록들과
https://stackoverflow.com/questions/5343131/what-is-the-sessions-secret-option
여기에서 Michael Mior라는 분이 불평하는 것으로 볼 때 예전에는 secret을 직접 수동으로 바꿔줘야 했지만, 1.11.0 부터는 secret rotation이라는 것을 지원해서 편해졌다네요!
여러 개의 secret을 string의 배열 형태로 동시에 제공할 수 있게 함으로써 기존 세션을 무효화시키지 않고 안전하게 secret을 변경할 수 있는 방법을 제공합니다. secret 배열이 제공될 경우 첫 번째 요소는 새로운 세션을 서명하는 데에 사용되고, 나머지 요소들은 서명을 검증하는 데에만 사용됩니다. 이를 통해 최신 secret으로 새로운 세션을 생성하면서도 이전 secret으로 생성된 세션을 여전히 유효하게 유지할 수 있습니다.
잘 모르지만 길면 오버헤드가 심해지지 않을까요?
Spring Security에 비록 express-session과 비슷한 직접적인 cookie signing 메커니즘은 없지만, Remember-me와 같은 다른 방법들로 cookie의 무결성을 보장한다고 합니다.
secret은 쿠키 암호화를 위한 것이 아닙니다!
256비트짜리 문자열을 사용해봅시다!
최종 코드는 아래와 같은 형태를 띄겠네요.
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production', // use HTTPS in production
maxAge: 60000 // example value, set based on your needs
}
}));
저의 연구가 많은 도움이 되셨길 바랍니다!