JWT(Json Web Token)은 토큰 기반 인증에서 사용하는 대표적인 토큰으로서 헤더(Header), 페이로드(Payload), 서명(Signature) 세 부분으로 이루어져있다. 각 부분을 Base64로 인코딩하고 마침표를 이용하여 차례대로 연결한 것이 JWT다.
헤더는 토큰의 타입을 지정하는 type과 암호화 알고리즘을 지정하는 alg 값을 갖는다.
페이로드는 로그인안 사용자의 상태 정보를 갖는 JSON 값이다. 발급자와 유효 기한 등의 정보가 담기며 각 정보의 조각을 클레임(Claim)이라 부른다.
헤더와 페이로드를 Base64로 인코딩하고, 이 둘을 마침표로 연결한 것을 서버의 비밀 키와 함께 암호화 알고리즘에 넣어서 돌리면 서명을 얻을 수 있다. 서명은 서버가 토큰의 유효성을 검증하는데 사용한다.
서명은 악의적 목적의 토큰 변조를 방지한다.
Access Token은 요청에 함께 보내 사용자가 로그인을 했는지 확인해주는 역할을 한다.
인가(Authorization) 의 과정이라고 볼 수 있다. HTTP 프로토콜의 Stateless 특성상 서버는 사용자를 기억할 수 없기 때문에, 로그인 되어 있는 사용자의 상태를 기억하기 위해 Access Token을 사용하는 것이다.
*인가 : 로그인한 사용자가 서비스의 특정 기능을 사용하기 위한 권한을 허가받는 절차
Refresh Token이 등장하게 된 배경은 크게 두 가지다.
1. Access Token 만료시마다 로그인해야하는 불편함을 보완하기 위해
2. Access Token을 자주 발급함으로써 보안을 강화하기 위해
보통 Access Token의 유효 기간을 30분, Refresh Token의 유효 기간을 2주정도로 설정함으로써 사용자의 로그인이 오랫동안 지속되도록 한다.
토큰 기반 인증에서는 서버에서 전송한 두 토큰을 쿠키 혹은 로컬 스토리지에 저장한다.
Refresh Token은 요청시 서버에서 확인해야 하기 때문에 데이터베이스에 저장하는데, 나는 Redis에 저장했다.
쿠키는 HttpOnly 옵션이 있어 XSS 공격으로부터 비교적 안전하다.
로컬 스토리지와 같은 웹 스토리지의 데이터는 매 요청마다 서버에게 자동으로 전송되는 것이 아니기 때문에 CSRF 공격에 상대적으로 안전하다.
하지만 XSS 공격을 방지할 수 있는 보안 옵션이 없기 때문에 이스케이프 처리와 같은 다른 대응을 면밀히 해주어야 한다.
프로젝트에서는 Access Token과 Refresh Token을 둘 다 사용하였다. 유효 기간은 각각 30분, 2주로 설정했다.
클라이언트 쿠키에 Access Token과 Refresh Token Index를 저장하였고 Refresh Token은 Redis에 Index와 따로 저장하였다.