개발을 하다보면 다들 JWT에 대해 무조건 들어봤을 것이다. 나도 이번 프로젝트를 하면서 처음 JWT 인증 기능을 구현하게 되었는데, 개념에 대한 이해가 부족한 거 같아 이 글을 작성하게 되었다. JWT가 무엇이고, 왜 쓰는지에 대해 알아보자.
Json Web Token
의 약자로, 서버와 클라이언트 사이에서 JSON 객체를 안전하게 전송할 수 있는 방법을 제공하는 개방형 표준으로 인증에 필요한 정보들을 암호화시킨 JSON 토큰이다. 주로 웹 사이트의 인증에 사용된다.
JWT는 . 을 구분자로 세 부분으로 구성되어 있다.
헤더(Header)
: 토큰의 타입과 해싱 알고리즘 정보를 담고 있다.
{
"alg": "HS256",
"typ": "JWT"
}
토큰의 타입과 토큰을 서명하는데 사용한 알고리즘을 나타낸다.
페이로드(Payload)
: 실제 전달하려는 데이터를 포함하며, 클레임(claim)이라는 표현을 사용하여 나타낸다. 클레임에는 등록된 클레임, 공개 클레임, 비공개 클레임 등 여러 종류가 있다.
{
// 등록된 클레임
"iss": "kmu", //발행자
"sub": "123456789", //제목
"exp": "1659002265", //만료시간
// 개인 클레임
"userName": "dong",
"isAdmin": false
}
토큰에서 사용할 클레임들이 담겨있다. 여러 정보를 추가할 수 있지만, 보안에 민감한 것들은 추가하지 않는 것이 좋다.
시그니처(Signature)
: 헤더와 페이로드를 이용하여 생성되며, 이 과정에서 비밀 키가 사용된다. 이 시그니처는 토큰이 변조되지 않았는지 확인하는데 사용된다. 시그니처는 토큰의 마지막 부분에 추가되며, 토큰을 받는 측에서 이 시그니처를 검증함으로써 토큰이 변조되지 않았음을 확인할 수 있다. 헤더와 페이로드 값은 디코딩을 통해 누구나 알아낼 수 있지만 시그니처 값은 지정한 비밀키를 알아야만 구할 수 있다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
시그니처 검증 과정에 대해 알아보자
유저 A가 B 사이트에 로그인 하는 상황을 예로 들겠다.
ex) 발급받은 Jwt Token이 'xxxx.yyyy.zzzzz'라면 A가 B에 요청 전송시 Request Header의 'Authorization'에 'Bearer xxxx.yyyy.zzzzz'를 담아 전송
페이로드
에 유저 정보를 바꿔 다른 유저인 것 처럼 접근하려 해도 비밀키를 모르기 때문에 정확한 시그니처
를 만들 수 없다.이처럼 서버는 사용자를 인증한 후, 사용자의 식별자와 일부 정보를 페이로드에 담아 JWT를 생성하고 이를 클라이언트에게 반환한다. 이후 클라이언트는 이 JWT를 이용하여 서버에 요청을 보내게 되며, 서버는 이 JWT를 검증하여 사용자를 인증한다.
쿠키와 세션이 존재하는데 왜 JWT를 사용하는 것일까? 먼저 쿠키와 세션에 대해 알아보자.
쿠키(Cookie)
는 클라이언트에 저장되는 작은 데이터 조각이다. 클라이언트가 웹 서버에 요청을 보내면 웹 서버는 HTTP 응답 헤더를 통해 정보를 Set-Cookie에 담아 클라이언트에 쿠키를 전달한다. 이후 클라이언트는 매 요청마다 이 쿠키를 HTTP 요청 헤더에 포함시켜 서버로 전송한다. 쿠키는 사용자의 로그인 정보, 방문 횟수, 이전에 방문한 사이트 정보 등을 저장하는 데 주로 사용된다.
세션(Session)
은 서버에 저장되는 데이터이다. 주로 보안에 민감한 정보들을 저장한다. 세션은 클라이언트가 서버에 접속하는 동안 유지되며, 클라이언트가 서버로부터 연결을 끊으면 세션은 종료된다. 로그인한 사용자의 정보, 장바구니 정보 등 클라이언트 별 정보를 저장하는 데 주로 사용된다.
얼핏 봐서는 유용한 기능같아 보이지만 쿠키와 세션 모두 보안에 취약하다. 쿠키는 클라이언트 측에서 저장되고 관리되므로, 사용자에 의해 조작될 수 있다. 세션 역시 악성 유저가 세션 ID 자체를 탈취하여 클라이언트인척 위장할 수 있다. 그리고 세션은 서버에 저장되므로, 사용자가 많아질수록 서버의 메모리 부하가 커진다.
쿠키와 세션의 단점들 때문에 JWT를 사용하기 시작했다. 이제 JWT의 장점과 단점에 대해 알아보자.
(Stateless)
서버는 클라이언트의 상태를 유지할 필요가 없다. 서버는 단지 JWT가 유효한지만 확인하면 돼 서버의 부하를 줄일 수 있다.헤더
와 페이로드
를 가지고 시그니처
를 생성하므로 데이터 위변조를 막을 수 있다.반면 단점도 존재한다.
페이로드
정보가 제한적이다. 페이로드
는 복호화 시 누구나 정보를 확인할 수 있다. 따라서 유저의 중요한 정보들은 페이로드
에 넣을 수 없다.Stateless
특징을 가져 토큰은 클라이언트 측에서 관리하고 저장한다. 만약 토큰 자체를 탈취당한다면? 그 토큰의 유효시간만큼 악성유저가 정보를 빼갈 수 있는 문제가 발생한다.JWT를 하나만 사용했을 경우 아까 말했듯이 탈취를 당하면 토큰의 유효시간 동안은 정보들을 다 빼낼 수 있다. 그래서 현업에서는 Access Token, Refresh Token 으로 2개를 나누는 방식을 주로 사용한다.
Access Token
: 사용자가 서비스에 접근하는 데 필요한 토큰이다. 사용자가 어떤 리소스에 접근할 수 있는지를 정의한다. 클라이언트에서 요청이 오면 해당 토큰에 있는 정보를 활용하여 사용자 정보에 맞게 응답을 진행한다. 하지만 보안상의 이유로 Access Token의 유효 기간은 대체로 짧다.
Refresh Token
: Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위해 사용하는 토큰이다. Refresh Token은 일반적으로 Access Token보다 오랫동안 유효하다. 이 토큰을 사용해서 사용자는 로그인 절차를 다시 거치지 않고도 새로운 Access Token을 얻을 수 있다. Refresh Token은 사용자가 서비스에 계속 로그인된 상태를 유지하도록 도와준다.
Access Token
의 유효 기간을 짧게 설정함으로써 토큰이 탈취당해도 공격자가 오랫동안 서비스에 접근하는 것을 막을 수 있고, Refresh Token
을 통해 사용자는 로그인 상태를 유지할 수 있다.
잘못된 부분이 있으면 댓글 부탁드립니다!
참고자료 : https://inpa.tistory.com/entry/WEB-📚-JWTjson-web-token-란-💯-정리