왜 이런짓을 하는거지?
유저가 로그인을 하면, 인증이 된다. 근데 그 뒤에는?
클라이언트가 페이지를 이동하면? 어떻게 그 인증정보를 유지할 수 있을까?
HTTP요청은 Stateless이다. 즉, 이전 통신 내용을 기억하지 못한다.
그래서 100번 통신을 하면 100번 모두 0422임을 증명해줘야 한다.
그럼 100번 통신하면 100번 다 로그인할꺼냐? 그건 좀 아닌 것 같다.
이를 위한 방법으로는 세가지가 있는데 쿠키만사용, 세션/쿠키, JWT가 있다.
쿠키
사용자가 로그인을 하면 서버에서는 사용자 정보를 담은 쿠키를 생성해서 응답값으로 던져준다.
클라이언트는 이를 받아서 브라우저 쿠키형태로 저장을 해두고, 요청시마다 header에 담아서 서버로 날려준다.
근데인증을 위해서는 계정정보를 쿠키에 담고 있어야 한다.
이러면 네트워크 요청 도중에 쿠키를 탈취당할 경우, 계정정보가 그냥 다 노출되어서 해킹이 쉽고, 이로 인해 개인정보가 다 노출되게 된다.
세션/쿠키
세션은인증된 유저의 상태를 DB에 저장해 놓는 것이다.
1. 로그인시에 회원 DB를 검증해서 로그인에 성공하는 경우, 세션을 생성해서 세션 DB에 저장해놓는다.
2. 이를 검증하기 위한 값인 세션ID를 클라이언트에게 보내준다.
3. 클라이언트는 이를 쿠키로 저장한다.
4. 요청시 헤더에 쿠키를 담아 보낸다.
5. 서버는 이를 검증하여 적절한 응답을 한다.
이 방법은 좋지만 두 가지 문제가 있다.
- 서버에 저장해야한다. 이로 인해 또 다시 두 가지 문제가 발생한다.
- 저장 공간의 용량 : 유저가 한 두명이면 괜찮지만 수천명이면 메모리 부하가 걸린다.
이를 방지하기 위해 DB에 따로 저장하는경우, DB IO가 발생하기때문에 성능상 이슈가 발생할 수 있다. - 확장성의 문제 : 서버를 여러곳으로 확장, 분산하는 경우 세션 분산 기술을 따로 설계해야 한다.
- 저장 공간의 용량 : 유저가 한 두명이면 괜찮지만 수천명이면 메모리 부하가 걸린다.
- 로그아웃을 하지 않는 경우, 쿠키형태로 저장된 세션아이디를 탈취당하면 그대로 해킹당할 수 있다.
JWT란?
JSON Web Token의 약자로, 전자서명된 JSON이다.
전자서명은 간단히 말하면 원문을 해시한 값을 암호화하여 원문과 함께 전송하고, 이를 받은 뒤에 복호화하여 해시한 원문과 비교하여 그 무결성을 보증하고 부인방지를 할 수 있게 하는 방법이다.
JWT토큰은 이렇게 생겼다.
.을 기준으로 3부분으로 구성된다.
파란부분은 header
빨간부분은 data
노란부분은 signature 되시겠다.
이칭구를
https://jwt.io 에 넣어보면 일케 decoded해준다.
Header
해싱 알고리즘과 type이 적혀있다.
이 해싱알고리즘으로 아래의 시그니처를 만든다.
Payload
내가 로그인한 유저임을 증명할 수 있는 기본적인 정보들을 넣는다.
Signature
헤더와 페이로드를 합친 문자열을 암호화하여 만든다.
어? 근데 jwt.io에서 저게 decoded되면 안되는거 아님?
디코드가 되어 정보를 얻을 수 있다고 해도, 이를 위조할 수는 없다.
JWT는 보통은 대칭키로 암호화되고, 복호화된다. 이때 대칭키는 서버만 가지고 있어서 그 키로만 유효한 JWT를 만들어 낼 수 있다.
즉, 헤더와 페이로드를 얻는건 쉽지만, 그걸 수정해서 보내기란 어렵다.
변조가 불가능한 token이 되는 것이다.
어 ? 근데 그럼 검증은 어떻게 해요?
디코드하면 내용을 확인할 수 있다. 여기서 헤더와 페이로드를 얻을 수 있다.
이걸 합친다음 header에 적힌 알고리즘으로 해시시키면 signature가 나오게된다.
이게 똑같다는 것은 header와 payload가 변경되지 않았다는 것이므로 payload를 신뢰할 수 있게 된다.
그말인 즉슨, 따로 DB에 세션처럼 저장하지 않아도 쉽게 식별이 가능해 진다는 것이다.
JWT가 짱인가?
어느 부분에서는 그렇고, 어느부분에서는 아니라고 생각했다.
예를들어 넷플릭스의 사용자 접속 제한을 떠올려보면, 몇명 이상 같은 세션으로 접속하지 못해야 하므로 JWT보다는 세션/쿠키 방식이 구현하기 더 쉬울 것이다.
서비스의 특성을 생각해서 구현하면 더 좋을 것같다는 생각이 들었다.
보안면에서도, 토큰을 탈취당하면 유효기간(payload의 exp)이 끝나기 전까지 해당 사용자 계정에 계속해서 접근할 수 있다. 그래서 유효기간을 짧게 설정하는 것이 좋고, refresh토큰을 따로 사용하여 갱신해주는 방식이 좋다.