이전에는 세션 방식(서버 인증 방식) 을 이용해서 로그인 기능을 구현했지만,
최근엔 JWT라는 토큰 기반의 인증 방식이 나오면서 웹과 모바일에서 대부분의 인증이 JWT로 구현되고 있다. JWT(Json Web Token)는 클레임 토큰 기반 인증 방식으로 인증에 필요한 정보를 암호화시켜 토큰 body에 저장함으로써 클라이언트가 증명서처럼 사용한다.
세션과 비교하여 JWT
JWT는 세션과 비교하였을 때 무상태(stateless)와 확장성(scalability)의 이점이 있다.
토큰은 클라이언트에서 저장하기 때문에 서버측에서는 stateless하며, 요청이 왔을 때 서명이 옳바른지만 확인하면 되므로 비저장의 이점을 지닌다.
또한 msa와 같은 서버가 여러대로 분리되어 사용되고 있다면 어떤 유저가 로그인 했을때 그 유저가 처음 로그인했던 서버에만 요청을 보내야 하기 때문에 확장성에 제약이 있지만 토큰방식을 사용한다면 어떤 서버로 요청이 오더라도 관계없이 인증할 수 있다.
또한 보안적으로도 기존의 세션 방식의 HTTP 요청을 해커가 가로챌 경우 그 안의 쿠키를 이용해 악용될 수 있는 보안적 약점을 보완했다.
JSON Web Token (JWT) 은 웹표준 (RFC 7519) 으로서 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인 (self-contained) 방식으로 정보를 안전성 있게 전달해준다.
Refresh token과 Access token 두 가지를 사용한다는 전제
클라이언트에서 로그인 요청(Post)
서버에서 요청이 들아온 로그인 정보와 DB에 저장된 유저 정보와 비교
Access Token을 발급하여 클라이언트에 JWT를 반환
클라이언트에서 Access Token을 가지고 있다가 인가가 필요한 요청(댓글 작성, 회원정보 수정 등)을 할 때 Header에 담아 전달
Access Token이 만료됐다면 서버에 있는 Refresh Token이 유효한 지 확인 후 Access Token 재발급
재발급한 Accss Token을 헤더에 담아 다시 서버에 요청
서버에서 토큰과 맞는 유저 정보가 있는지 확인(디코딩 과정?)
찾은 유저 정보와 요청한 유저가 일치하는지 확인.
일치하면 Request에 맞는 Response를 반환하고, 만료된 토큰이거나 잘못된 토큰이면(일치하는 유저가 없으면) 401 Unauthorized 리턴
JWT 페이로드 부분에는 사용자를 특정할 수 있는 정보가 들어가면 안 된다. 페이로드는 사용자를 식별할 수 있는 정보를 담고 있는데, 암호화가 아닌 단순 BASE64 인코딩으로 만든다. 누구나 쉽게 디코딩할 수 있기 때문에 아이디나 이메일 등 사용자 개인정보를 페이로드에 담아 인코딩한다면 개인정보가 유출될 우려가 있다. 따라서 DB에 저장된 유저의 PK값 같이 디코딩된 정보만 봐서는 어떤 사용자인지 알 수 없는 정보를 담아야 한다.
참고) payload에 들어가는 클레임 설명 (필수 아님. 모두 선택사항)
iss
: 토큰 발급자(issuer)sub
: 토큰 제목(subject). 토큰이 갖는 문맥aud
: 토큰 대상자(audience). 토큰을 사용할 수신자exp
: 토큰의 만료시간(expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정해야 함nbf
: Not Before를 의미하며, 토큰의 활성 날짜와 비슷한 개념. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜 이전에는 토큰을 처리하지 않아야 함을 의미iat
: 토큰이 발급된 시간(issued at), 이 값으로 토큰의 age가 얼마나 되었는지 판단jti
: JWT의 고유 식별자로, 중복 처리를 방지하기 위하여 사용. 일회용 토큰에 사용하면 유용대표적 JWT 변조 공격은 Signature Stripping인데 헤더의 alg
클레임을 None
으로 변조하는 공격으로, 일부 JWT 라이브러리들이 alg
가 None
인 토큰을 유효한 토큰으로 인식하는 문제가 있다. 또는 웹 게시판 등에 사용자가 입력한 값이 DB에 저장되고, 프론트엔드단에 출력하는 구조를 가진 페이지에서 공격자가 <script>
태그를 입력하면 공격이 성립할 수 있다. 이때 일반 쿠키 또는 세션스토리지에 저장한 토큰이 탈취당할 수 있다.
이를 보완하는 방법 첫 번째는 Refresh Token입니다. 사용자가 로그인할 때 Access Token과 함께 Refresh Token을 발급하는 것입니다.
클라이언트는 Access Token이 만료되었다는 오류를 받으면 따로 저장해두었던 Refresh Token을 이용하여 Access Token의 재발급을 요청한다. 서버는 유효한 Refresh Token으로 요청이 들어오면 새로운 Access Token을 발급하고, 만료된 Refresh Token으로 요청이 들어오면 오류를 반환, 사용자에게 로그인을 요구한다.
Access Token은 서버에 따로 저장해 둘 필요가 없지만, Refresh Token은 서버에 저장해 Access Token 재발행 시 검증에 활용해야 한다. 그러므로 Refresh Token을 이용한다는 것은 추가적인 I/O(Input/Output) 작업이 필요하다는 의미이며, 이는 I/O 작업이 필요없는 빠른 인증 처리를 장점으로 내세우는 JWT의 스펙에 포함되지 않는 부가적인 기술이다.
그 외에도 토큰 자체를 암호화 하기, HTTPS 통신에서만 쿠키를 전송할 수 있게 하기, 기기 로그인 IP 등을 토큰에 넣는 것도 방법이 될 수 있다.
참고자료
https://velopert.com/2389
https://ooeunz.tistory.com/74
https://naon.me/posts/til63