주요 내용으로 건너뛰기

공부: session-cookie vs jwt auth

두 인증 방식의 차이와 클라이언트와의 결합

+ 2018.12.13 추가: cookie 전달을 위해 credentials를 설정하는 부분에 대한 설명을 정정합니다.

Fetch API와 함께 cookie를 전달해야하는 경우,  credentials 옵션을 헤더에 설정하지 않으면 “omit” 모드로 설정이 됩니다.

이는 fetch API의 스펙(https://fetch.spec.whatwg.org/#concept-request-credentials-mode)에 명시되어 있으며 (Unless stated otherwise, it is "omit”.),

그렇기 때문에 cookie를 전달하려면 다음의 두 가지 옵션 중 하나를 매뉴얼 하게 설정해줘야합니다.


1. Same-origin: 만약 요청 url이 같은 origin을 사용한다면 cookie를 포함.

2. Include: 항상 요청에 cookie를 포함.


그러므로 서버의 origin과 요청을 보내는 클라이언트의 origin을 확인할 필요가 있습니다.

예를 들어 클라이언트의 url이 example.com:80이고, 서버의 url이 example.com:3000이라면

포트 넘버가 일치하지 않으므로 cross-origin 요청이 됩니다. 

이 경우에는 ‘include’ 옵션을 사용해야 합니다. 


Fetch API에 대한 MDN 문서: https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Fetch%EC%9D%98_%EC%82%AC%EC%9A%A9%EB%B2%95

스택오버플로우 질문: https://stackoverflow.com/questions/34558264/fetch-api-with-cookie

credentials 옵션에 MDN 대한 문서: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials


[본문]

지금까지 node js - react js 를 배워오면서 가장 이해가 어려웠던 부분은 결국 인증 파트였다.

대표적인 인증 모듈인 passport js 를 접한 것을 계기로 여러가지 에러들을 겪으면서 조금씩 이해해왔는데

처음부터 궁금했고 얼마전까지도 제대로 이해하지 못했던 메커니즘이 있었다.

그 중 하나가 세션-쿠키 기반 인증 방식이 어떻게 클라이언트와 결합할 수 있는가였는데,

그 이유는 대부분의 유튜브 비디오나 블로그의 설명이 node js 앱을 기준으로 설명했기 때문이다.

Node js 앱의 경우 인증 미들웨어를 통해 passport js 에서 넘겨주는 req.user를 받아서 인증여부를 따질 수 있지만 브라우저 환경에서는 그 방법을 사용할 수가 없다. 

왜냐하면 만약 브라우저 쿠키에 민감한 정보(세션 키나 유저의 개인정보 등)를 넘겨준다면 보안 취약점이 발생하기 때문이다. 

그래서 쿠키는 httpOnly flag와 max-age flag를 통해 자바스크립트로 컨트롤할 수 없고 동시에 일정 시간이 지나면 만료되게 설정해야된다.

다만 내가 문제라고 생각했던 부분은 httpOnly flag를 세웠을 때 document.cookie를 통해 쿠키를 조작할 수 없게 된다면 어떻게 헤더에 쿠키를 포함해서 요청을 보낼 수 있는가하는 것이었는데,

답은 가까이에 있었다.

답은 credentials 옵션을 설정해주면 쿠키를 담아서 전송해준다는 것이었다. (상세한 내용은 위의 추가된 내용을 확인해주세요.)

굉장히 허무했지만 결국 모든 원인은 기본이 되는 쿠키와 같은 개념을 제대로 이해하지 못한데 있을 것이다.

적어도 mdn 문서를 한 번이라도 정독했으면 이런 일은 안 일어났을텐데 ㅜ

세션-쿠키 인증을 이해하는 핵심은 결국 http라는 stateless한 녀석과 server-client가 어떻게 정보를 공유할 수 있게 만드는가 인것같다. 서버에서는 민감한 정보에 대한 키인 세션키를 생성, 쿠키의 형태에 클라이언트에 발급하고, 클라이언트에서는 http요청을 할 때마다 자신이 인증 사용자임을 밝히기 위해 상태정보를 담은 쿠키를 헤더에 담아 보내는 것이다. 

마지막으로 세션-쿠키 인증에서 유의해야 하는 것은 xss/csrf 공격인데

xss 공격의 경우 httpOnly쿠키를 통해 방지할 수 있고, csrf 공격의 경우 node js의 csurf 모듈을 통해 관리할 수 있다.

get요청이 아닌 post delete put 등 데이터에 대한 수정이 가해지는 요청의 경우 적절한 csrf 토큰을 요구한다면 이를 예방할 수 있다.


다음은 jwt 인증 방식이다. jwt는 json web token의 줄임말로 서버에서 세션을 저장하는 대신 

브라우저에(localStorage / sessionStorags / cookie) 토큰(토큰/리프레쉬 토큰)을 저장하게끔하고

http 요청을 보낼 때마다 그 토큰을 헤더에 담아 서버에 특정 api 호출을 하는 방식이다.

세션-쿠키 인증 방식에 비해 scale out 하기가 쉽고 서버에 부담이 덜한 것이 특징이다.

또한 자바스크립트로 컨트롤 할 수 없는 httpOnly 쿠키와는 달리 localStorage api 는 자바스크립트로 컨트롤이 가능하다.

거기에 인증 방법 또한 node js 의 jsonwebtoken 모듈을 이용하면 굉장히 간편하다. 

다만 토큰을 서버측에서 폐기할 수 있는 방법이 전혀 없고, 그로 인해 토큰을 탈취당했을 때 굉장히 보안 취약점을 드러낸다.

그럼에도 토큰의 age를 짧게 설정한다면 크게 걱정할 사안은 아니라고 생각한다.


두 가지 방식 모두를 사용해본 결과 내가 만드는 앱들은 대부분 학습용으로 만든 것들이었기 때문에 편의성 측면에서는 어느 쪽이 확실하게 낫다는 생각은 들지 않았다.

그럼에도 두 가지 방식 모두 구현이 크게 어렵지 않았지만 조금 더 편리한 쪽을 고르라면 역시 jwt 토큰 방식이 아닐까 싶다.


이준형 님의 창작활동을 응원하고 싶으세요?

댓글 1

SNS 계정으로 간편하게 로그인하고 댓글을 남겨주세요.