JWT 인증 방식을 왜 사용해야할까?
우리는 로그인을 통한 사용자 인증을 구현해야 한다. 그런데 왜 인증을 해야할까?
먼저 HTTP의 Stateless 특성을 통해 로그인할때 왜 인증을 해주어야 하는지에 대해 살펴보자.
그리고 쿠키, 세션, 토큰 인증 방식을 비교하고 결국에 왜 토큰 방식 중 JWT가 선택되었는지도 알아보자.
HTTP의 두가지 특성
1. connectionless
HTTP는 서버와 브라우저간의 연결을 계속 유지하지 않는다는 뜻이다.
서버와 클라이언트 사이의 통신이 끝나게 되면 연결이 끊긴다.
2. stateless
상태를 유지하지 않는다. 첫번째 연결에서의 상태가 두번째 연결까지 유지되지 않는다. 이전 상태가 저장되지 않고 연결마다 갱신된다. 이 때문에 처음 로그인한 상태가 계속 유지가 되지 않는다는 문제점이 발생한다.
요약하자면 HTTP stateless의 문제점 때문에 로그인시 매번 인증을 해줘야 한다.(로그인 상태가 유지가 되지 않기 때문에 어떤 요청이 생길때마다 계속 로그인을 해줘야 된다... ) 매번 인증을 해준다면 당연히 부하가 생길 것이고 아무리 생각해도 정상적인 흐름이 아닌것 같다. 이를 해결하기 위해 로그인을 유지해주는 방법 3가지가 존재한다.
쿠키, 세션, 토큰으로 세가지가 존재하며 사용자 정보를 브라우저에 저장하는 방식, 서버에 저장하는 방식, 토큰으로 암호화해 그때 그때 복호화하는 방식으로 나뉜다. 아래는 각 인증 방식을 소개하고 왜 토큰을 사용하게 되었는지에 대해 설명한다.
1. Cookie 인증
- 쿠키는 key-value 형식의 문자열이다.
- 클라이언트가 브라우저에 방문할 경우, 서버를 통해 브라우저에 설치되는 작은 기록 정보이다.
- 각 사용자마다 브라우저에 정보를 저장하므로 사용자 정보 식별이 가능해진다.
쿠키 인증 과정
- 브라우저가 서버에 요청을 보낸다.
- 서버가 Response할 때 브라우저에 저장하고 싶은 정보를 헤더의 Set-Cookie에 담는다.
- 브라우저가 서버에 Request할 때마다 저장된 쿠키를 Request 헤더의 Cookie에 담아 보낸다. 서버는 쿠키의 정보를 통해 요청한 클라이언트가 누군지 식별한다.
Cookie 방식의 단점
- Request시 쿠키값을 그대로 보내기에 유출/조작될 위험이 있다.
- 쿠키는 용량제한이 있어 많은 정보를 담을 수 없다.
- 브라우저마다 쿠키 형태가 달라서 브라우저간 쿠키 공유가 불가능하다. (크롬, 웹브라우저..)
- 쿠키 사이즈가 클수록 브라우저의 부하가 심해진다.
2. Session 인증
쿠키의 보안적 이슈를 해결하기 위해 서버측의 민감한 사용자의 정보를 저장하고 관리한다.
서버에서 처리하기 때문에 보안이 강하지만 서버의 자원을 지나치게 사용한다는 단점이 존재한다.
세션 인증 과정
- 유저가 로그인하면 세션을 Session Id를 기준으로 서버 메모리 상에 저장한다. Session Id는 세션을 식별하기 위함이다.
- 서버에서 브라우저 내의 쿠키에 Session Id를 저장한다.
- 브라우저는 모든 Request에 Session Id를 쿠키에 담아 전송한다.
- 서버는 보내진 Session Id와 서버 메모리에서 관리되는 Session Id를 비교해 인증한다.
Session 방식의 단점
- 세션 ID 자체는 유의미한 정보를 가지지 않는다. 하지만 세션 ID 자체를 탈취해 클라이언트인 척하여 인증받을 수 있다는 단점이 존재한다.
- 또한 서버의 세션 저장소에 저장하므로 Request가 많아질수록 서버에 부하가 생긴다.
3. 토큰 방식
사용자가 로그인을 하면 서버에서 토큰을 발행해주고 클라이언트 측의 저장소에 토큰을 유지시킨다. 해당 토큰이 바로 JWT(json token)이다.
서버에 저장하지 않기 때문에 부하를 줄여주고, 클라이언트측에서는 해당 토큰이 유효한지만 체크하면 된다.
서버가 클라이언트에게 토큰을 발행해주면 클라이언트는 Request마다 헤더에 토큰을 함께 보낸다. 이후 서버에서 브라우저의 토큰과 서버의 토큰이 일치하는지 확인한다.
토큰 인증 과정
- 사용자가 로그인한다.
- 서버에서 유일한 토큰을 발행한다.
- 클라이언트는 받은 토큰을 쿠키 또는 스토리지에 저장한다. Request마다 해당 토큰을 담아 보낸다.
- 서버는 클라이언트의 토큰을 검증한 뒤 Response한다.
그렇다면 기존의 토큰방식과 JWT 방식의 차이는 뭘까?
기존의 토큰 방식은 사용자 정보를 담은 Payload가 암호화되지 않기 때문에 중요한 정보를 담을 수 없다. 또한 토큰을 탈취당하면 대처하기 어렵다. 그러므로 Payload를 암호화하고, 토큰의 제한시간을 두어 해당 문제점들을 극복한 것이 JWT 방식이다.
JWT (JSON Web Token)
JWT는 인증에 필요한 정보들을 암호화시킨 JSON 토큰을 말한다. JSON 데이터를 Base64 URL-safe Encode를 통해 인코딩하여 직렬화한 것이며 토큰 내부에는 개인키를 통한 전자서명도 들어있다.
기존 토큰 방식은 일반 토큰을 사용하고, JWT는 암호화된 Access Token을 사용한다. 또한 JWT는 Refresh Token을 통해 Access Token을 재발급받을 수 있게 한다.
JWT 구조
JWT는 .를 구분자로하는 세 문자열의 조합이다. Header, Payload, Signature 세가지로 구성이 된다.
- Header는 토큰 타입(JWT)과 해시 알고리즘의 종류가 담겨 있다.
- Payload는 사용자 권한 정보와 r같은 실제 데이터가 들어 있다.
- Signiture에는 Header와 Payload를 첫번째로 Base64 URL-safe Encode를 하고 두번째로 Header에 명시된 해시함수를 적용하고, 세번째로 개인키로 서명한 전자서명이 담겨있다. (3번의 암호화된 서명)
Hedaer
{
"alg" : "HS256", // 서명 암호화 알고리즘
"typ" : "JWT" // 토큰 유형
}
Payload
토큰에서 사용할 정보의 조각인 Claim이 담겨있다. 실제로 사용될 정보를 의미한다.
Claim은 Registed, Public, Private 3가지로 나뉜다.
- Registed Claims : 미리 정의된 클레임 ( iss, exp, sub, iat, jti )
- Public Claims : 사용자가 정의할 수 있는 공개용 정보 전달시 사용
- Private Claims : 당사자들 간 정보를 공유, 외부에 공개되도 상관없지만 유저를 특정하는 정보들을 담음
{
"sub" : "1234567890",
"name" : "soohey",
"iat" : "20211225"
}
Signiture
Header의 alg(알고리즘)을 사용하여 Header+Payload와 서버가 갖는 유일한 비밀키값을 암호화한다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
JWT는 비대칭 암호화 알고리즘을 사용하여 암호화키와 복호화키가 다르다. 암호화에는 개인키를, 복호화에는 공개키를 사용한다.
- 사용자가 로그인 요청을 한다.
- 서버에서 Header, Payload, Signiture을 정의한다. 각각 Base64로 암호화하여 JWT를 생성한 뒤 쿠키에 담아 클라이언트로 Response한다.
- 클라이언트는 받은 JWT를 로컬 스토리지에 저장한다. 이후 Request마다 Authoriztion header에 Access Token을 담아서 보낸다.
- 서버는 Header에 담긴 JWT(Access Token)이 서버에서 발행한 토큰과 일치하는지 확인한다.(일치하면 인증성공) 인증이 통과되면 페이로드에 있는 유저의 정보를 찾아 클라이언트에 되돌려준다.
- 만약 Access Token의 시간이 만료되면 클라이언트는 Refresh Token을 보내 서버로부터 새로운 Access Token을 발급받는다.
요약하면 JWT는 점(.)으로 헤더, 페이로드, 시그니쳐를 구분하여 전달되고, 서버는 헤더의 alg, kid 공개키를 이용해 검증한다.
검증이 성공하면 페이로드의 값으로 접근하여 사용자 인증이 완료된다.
Refresh Token이란?
우리는 Access Token을 도둑맞을 것을 대비해 유효시간을 짧게 해서 서버로 보내게 된다.
하지만 유효시간이 너무 짧아지면 사용자가 로그인을 여러 번해야하는 불상사가 생기게 된다.
이를 대비해서 Access Token을 재발급 받게하는 Refresh Token이 존재한다.
Access Token과 함께 동봉해서 서버로 보내게 되는데, 이 때 유효시간을 Access Token보다 더 길게 설정한다.
Access Token의 유효시간이 하루라면, Refresh Token은 일주일 정도로 주어서, Access Token의 유효시간이 끝나더라도 Refresh Token은 남아있기 때문에 Refresh Token을 통해 Access Token을 발급받을 수 있다.
JWT 장점
- 별도의 인증 저장소가 없어 서버와의 통신을 줄일 수 있으며 트래픽 부하가 적다.
- 데이터의 위변조를 막을 수 있다.
- 세션과 달리 서버는 Stateless하다. ( 인증 정보를 따로 저장하지 않기 때문에)
- 확장성이 우수하며, 쿠키와 달리 토큰 기반이므로 다른 로그인 시스템에도 접근/공유가 가능하다.
- DB조회를 안해도 되는 장점을 가져 서버 부하를 줄인다. ( payload에 유저이름과 권한을 같이 보내면, 서버에선 DB조회없이 바로 권한을 알 수 있음)
JWT 단점
- JWT는 요청마다 전송되므로 크기가 커질수록 데이터 트래픽에 부하를 준다.
- 클라이언트에 저장되므로 서버측에서 DB를 수정해도 토큰에는 영향을 줄 수 없다.
참고
http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
그 외
서비스를 작은 단위로 분할하여 개발하는 MSA가 주목받고 있다. 그에 따라 각 API들은 클라이언트에 대한 인증(Authentication)과 권한부여(Authorization)을 위한 매커니즘이 필요하다. JWT(JSON Web Token)는 MSA에 사용가능한 서명된 JSON이므로 서버측 부하를 줄여주고 접근권한 관리를 효율적으로 할 수 있다.
http://www.opennaru.com/opennaru-blog/jwt-json-web-token-with-microservice/