JWT란(Feat. Stateless)
JWT(Feat. Stateless)
세션에서 JWT로 전환한 이유
쿠키,세션 방식의 인증이 REST API의 특성인 Stateless엔 어울리지 않는다는 생각을 했다.
또한, 쿠키라는 개념이 브라우저에 종속되어있어(다른 곳에도 대체제가 있긴함) 다양한 기기와 통신할수있다는 비종속성이 최대 강점이자 목표인 REST API에도 반한다는 생각이 들었다.
따라서 Stateless한 JWT를 정리해보고자 한다.
현재 대부분의 서비스가 REST API를 사용하는 시점에서 Stateless한 방법을 사용하기 위해서는 어쩔 수 없이 지금과 같은 토큰의 방식의 인증 체계를 사용할 수 밖에 없기는 하다.
쿠키는 보안상 문제가 너무 많고, 세션은 요청을 할 때마다 필수적으로 세션 스토리지 I/O작업이 일어나야 하기때문
물론 JWT 조차도 완전하지는 않아서 위처럼 Sliding Session을 사용하거나 Refresh Token을 사용해 단점을 보완하고있긴 하지만 이것 조차 100% 완전하진 않다.
각 방법들이 한계가 존재함
그렇기 때문에 내 서비스가 JWT를 사용하기에 적합한가를 먼저 판단해야한다.
JWT
JWT란? 인증에 필요한 정보들을 Token에 담아 암호화시켜 사용하는 것
진행하는 구조는 Cookie때와 크게 다르지는 않지만, 강조되는 점은 JWT는 서명된 토큰이라 Stateless하다는 점이다.
헤더에 토큰을 담아서 던진다.
공개키/개인키 방식을 사용한다.
JWT 인증 방식
JWT 토큰은 아래와 같이 생겼다. "." 을 기준으로 3가지로 나눠진다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. //1.Header
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.//2.Payload
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c //3.Signature
1. HEADER
토큰의 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장
{
"alg": "HS256", //서명 생성에 사용된 알고리즘, (다양한 알고리즘이 사용됨)
"typ": "JWT" //토큰 타입(JWT 고정)
}
"Header", "Payload" , "서버에 있는 개인키값" 3개를 넣고 서명 생성에 사용된 알고리즘(예시에선 HS256)을 돌리면 서명값(Signature)이 나온다.
이 알고리즘은 단방향이라 서명값으로 입력값 페이로드나 헤더를 얻어낼순없다.
이 결과값이 토큰의 SIGNATURE 부분에 담긴 값과 같은지 확인해서 인증여부를 결정한다.
만약 페이로드가 수정되었다면 이 값이 안맞으니 인증되지 못한다.
인증되었다면 로그인한 회원으로써 인가받는다.
2. PAYLOAD
payload에는 보통 Claim이라는 사용자에 대한, 혹은 토큰에 대한 정보를 Json 형태로 저장한다.
- 누가누구에게 발급했는지
- 사용자 정보 공개 범위
- 유효기간
다만 특별한 암호화가 아니라 BASE 64로 디코딩할수있어 사용자가 조작할수있다.
header와 payload는 json이 디코딩되어있을 뿐이지 특별한 암호화가 걸려있는 것이 아니기 때문에 누구나 jwt를 가지고 디코딩을 한다면 header나 payload에 담긴 값을 알 수 있기때문에 민감한 정보는 담지 않는다.
따라서 Header , Signature와 같은 나머지 부분이 필요하다.
3. SIGNATURE
서명값으로 Signature는 서버에 있는 개인키로만 암호화를 풀 수 있으므로 다른 클라이언트는 임의로 Signature를 복호화할 수 없다.
인증과 인가?
구분 | 설명 |
---|---|
인증(Authentication) | ID와 비밀번호로 사용자 신원을 확인 카카오 로그인은 각 서비스에 사용자가 카카오계정으로 로그인할 수 있는 기능을 지원 서비스에서 각 사용자를 식별할 수 있는 고유한 회원번호 제공 참고: OpenID Connect 지원, 로그인 세션 대신 사용 가능한 ID 토큰 제공 가능 |
인가(Authorization) | 사용자 개인정보와 같은 자원(Resource)에 대한 접근 권한 획득 카카오 로그인은 사용자 동의를 통해 사용자 정보나 기능에 대한 접근 권한을 서비스에 부여 서비스에서 부여받은 권한은 카카오 로그인 시 발급되는 토큰에 부여되며, 토큰을 사용해 해당 사용자에 대해 다양한 카카오 API 요청 가능 카카오 API를 통해 카카오 플랫폼에 저장된 사용자 정보를 제공받거나, 특정 기능이나 동작을 요청할 수 있음 |
JWT 단점 과 보완법
JWT 단점
- 쿠키, 세션때와는 다르게 base64인코딩을 통한 정보를 전달하므로 전달량이 많다. 따라서 네트워크 전달 시 많은 데이터량으로 부하가 생길 수 있다.
- Payload에는 암호화가 되어있지 않기 때문에 민감 정보를 저장할 수 없다.
- 토큰이 탈취당하면 만료될 때까지 대처가 불가능하다.
보완법
- 각 방법이 한계가 존재해 해결법이 아니라 보완법이다.
때문에 토큰의 유효기간을 매우 짧게 설정한다. 그러나 짧은 유효기간은 사용자로 하여금 계속 로그인을 다시 해야한다는 불편함이 존재한다.
그래서 JWT는 여러가지 보완법들을 사용한다.
Sliding Session , Refresh Token 등
Refresh Token
- 가장 많이 쓰이는 대표적인 보완법
JWT를 처음 발급할 때 Access Token과 함께 Refresh Token이라는 토큰을 발급하여 짧은 만료시간의 불편함을 보완하는 방법
accessToken
- 짧은 유효기간(보통 30분)을 가진다
- 인가에만 사용된다.
refreshToken
accessToken이 만료되면 사용자는 refreshToken로 요청을 보내고 서버는 Refresh Token을 받아 서버의 Refresh Token Storage에 해당 토큰이 있는지 확인하고, 있다면 Access Token을 생성하여 전달
- 유효기간이 2주 ~ 한달
- accessToken을 재발급 받을때 쓴다.
- 서버에 저장되어 보관한다.
세션과 동일한거 아니야?
refreshToken을 통해 accessToken을 재발급 받을때 세션 동작 방식처럼 Refresh Token Storage을 조회해서 토큰이 있다면 accessToken을 발급한다고했다.
이로써 세션처럼 토큰 자체가 탈취되었다고 판단이 뒤면 Refresh Token Storage를 초기화하여 탈취된 토큰이 더 Refresh 못하도록 막는 등 세션처럼 탈취시 대응 할 수 있는 옵션이 생기지만, 세션 처럼 추가 저장소를 위한 메모리가 필요하다.
하지만 그렇다고 세션과 같다고 생각하면 안된다.
세션은 매 인증마다 세션 저장소와 I/O 작업을 거치지만,
Refresh Token을 사용할때만, 즉, access token이 만료 되어 재발급이 필요할 때만 I/O작업을 거친다.
세션방식은 30분동안 1만번 요청이 온다고했을때 1만번 I/O 작업수행
Refresh Token은 30분후 accessToken이 만료되어 재발급이 필요할때 1번 I/O 작업수행
I/O작업에 있어 세션 방식보다 훨씬 효율적이다.
세션과 JWT
Stateful 하다는 세션의 단점을 보완하기 위해 만들어진 JWT는 별도의 세션 저장소와 같은 저장소를 강제하지 않기 때문에 Stateless하여 확장성이 뛰어나고,
실제로 토큰만 있으면 어디서든 서버를 이용할수있기때문에 OAuth에 쓰인다.
ex) 카카오 로그인 연동 등
개인키로만 복호화 할수있는 signature를 통한 보안성까지 갖추고있다.
하지만 Stateless하기때문에 토큰이 탈취되면 대처가 불가능하다는 단점을 보완하기 위해 ,
Refresh Token 보완법을 사용하면 JWT 방식도 세션처럼 저장소가 필요하기때문에 Stateful 해지며 저장소를 위한 추가 메모리가 들지만,
세션과 달리 매 요청이 아닌 Access Token의 갱신이 필요할 때만 인증을 위한 I/O를 거침으로 훨씬 효율적이다.
이는 JWT는 토큰 자체에 서명을 포함하기 때문에 인증된 정보로써 매 요청마다 저장소를 확인(I/O작업)할 필요가 없기때문이다.
I/O은 꽤나 무거운 작업으로 최대한 줄이는게 효율적이다.
Reference
https://www.youtube.com/watch?v=TlWzEr4cXfc
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api
https://velog.io/@jinyoungchoi95/JWTJson-Web-Token-%EC%9D%B8%EC%A6%9D%EB%B0%A9%EC%8B%9D