들어가기
HTTP는 stateless와 connectionless 한 특성을 가지기 때문에, 상태와 접속 정보가 유지되지 않는다.
따라서, 서버에서 클라이언트를 식별(인증) 하기 위한 방법이 필요하다.
인증 방식에는 서버 기반의 인증 방식과 토큰 기반의 인증 방식이 존재한다.
JWT를 알아보기 전에 먼저 인증 방식에 대해 알아보자
💡 서버 기반 인증 방식 vs 토큰 기반 인증 방식
✔️ 서버 기반 인증 방식 : 세션(Session), 쿠키(Coockie)
서버 기반 인증 방식은 서버 측에서 클라이언트의 정보를 기억하기 위해 세션을 유지하고, 세션을 메모리나 디스크, 데이터베이스 등을 통해 관리하는 방법이다. 클라이언트로부터 요청을 받으면 상태 정보를 저장/유지해야 하기 때문에 stateful 한 구조이다.
인증 과정
- 서버가 클라이언트에게 로그인할 수 있는 웹 사이트를 제공한다.
- 클라이언트는 제공받은 웹 사이트를 통해 로그인 요청을 보낸다.
- 서버는 클라이언트가 올바른 사용자임이 확인되면 세션을 생성하여 세션 Id를 부여한 후, 세션 저장소에 저장한 뒤 클라이언트에게 발급한다.
- 클라이언트는 세션 ID를 받아 쿠키에 저장하고, 인증이 필요한 리소스마다 쿠키에 세션 Id를 담아 요청을 보낸다.
- 쿠키(Cookie) : 브라우저에 저장되는 정보
- 서버는 쿠키를 받아 세션 저장소와 비교해서 올바른 요청인 경우 인증이 완료되고 요청에 응답한다.
문제점
서버 기반 인증 방식은 소규모 시스템에서는 아직 많이 사용되고 있지만, 몇 가지 문제점이 존재한다.
- 서버에서 클라이언트의 상태를 모두 저장해야 하기 때문에 클라이언트 수가 많으면 메모리나 DB 부하가 심하다.
- 멀티 디바이스 환경에서 로그인 시, 중복 로그인 처리가 되지 않는다.
- 클라이언트가 많아지게 되면 로드 밸런싱을 사용한 서버 확장이 필요한데, 이때 세션의 관리가 어려워진다.
- 로드 밸런싱(Load Balancin) : 서버가 처리해야 할 업무/요청(Load)을 여러 대의 서버로 나누어(Balancing) 처리하는 것
이러한 문제점의 해결 방안으로 나온 것이 토큰 기반 인증 방식이다.
✔️ 토큰 기반 인증 방식 : JWT
토큰 기반 인증 방식으로 많이 사용되는 것이 JWT이다.
토큰 기반과 서버 기반 방식의 가장 큰 차이점은 클라이언트 정보가 서버에 저장이 되는가 안 되는가이다.
토큰 기반 인증 방식은 클라이언트 정보가 서버에 저장되지 않는다.
토큰 기반 인증 방식은 인증받은 사용자에게 토큰을 발급해서 서버에 요청을 할 때 HTTP 헤더에 토큰을 담아 보내고, 인증받은 사용자인지 확인하는 방식이다. 서버 기반 인증 방식과 달리 정보를 서버에 저장하지 않고 클라이언트의 요청만으로 인가를 처리하기 때문에 stateless 한 구조이다.
인증 과정
- 서버가 클라이언트에게 로그인할 수 있는 웹 사이트를 제공한다.
- 클라이언트는 제공받은 웹 사이트를 통해 로그인 요청을 보낸다.
- 서버는 올바른 사용자임을 확인하고, 클라이언트에게 AccessToken을 발급해 준다.
- 클라이언트는 발급받은 AccessToken을 저장하고, 인증이 필요한 리소스마다 HTTP 헤더에 토큰을 담아 요청을 보낸다.
- 서버는 요청받은 암호화된 토큰을 복호화해서 올바른 요청인지 확인하고, 인증이 완료되면 응답한다.
장점
- 토큰은 클라이언트 측에 저장되기 때문에 서버는 완전히 stateless 한 상태가 되고, 클라이언트와 서버의 연결고리가 없기 때문에 확장성이 뛰어나다.
- 쿠키 사용에 대한 취약점이 사라지게 된다. 하지만 토큰 환경의 취약점이 존재할 수 있기 때문에 이를 대비해야 한다.
- 토큰 기반 인증 방식을 사용할 경우, 토큰에 선택적인 권한만 부여해서 발급할 수 있기 때문에 로그인에 대한 확장성이 커진다.
- OAuth2.0 기반의 로그인 방식을 사용할 수 있다. (Ex. Google 로그인, Kakao 로그인 등)
단점
- 서버 기반 인증 방식은 해당 세션이 악의적으로 사용될 경우 지워버리면 되지만, JWT는 한 번 발급되면 만료 기간이 완료될 때까지 계속 접근할 수 있기 때문에 악의적으로 사용될 수 있다.
즉, 서버 기반 인증 방식을 해결하기 위해 나온 방법이 토큰 기반 인증 방식이지만
이 방법도 보안으로부터 완전히 안전한 방법은 아니다.
💡 JWT(Json Web Token)란?
✔️ JWT
JWT(Json Web Token)는 Json 포맷을 이용해서 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token이다.
- JWT는 토큰 자체를 정보로 사용하는 Self-Contained 방식으로, 정보를 안전하게 전달한다.
- JWT는 Json 데이터를 Base64 URL-Safe Encode 방식을 통해 인코딩하여 직렬화한 것이다.
- 토큰 내부에는 위변조 방지를 위한 전자서명이 들어있다. 그렇기 때문에 사용자가 토큰을 서버에 보내면 서버는 서명을 검증하는 과정을 거치고, 검증이 완료되면 응답한다.
✔️ JWT 구조
JWT는 헤더(Header), 내용(Payload), 서명(Signature) 세 영역으로 구성되어 있다.
각 영역은 인코딩 되어 표현되고 점(.)으로 구분한다.
1. 헤더(Header)
헤더는 alg, typ 두 가지 정보로 구성된다.
- alg : 서명 암호화 알고리즘(Ex. HMAC, SHA256, RSA, ... ), 서명 및 토큰 검증에 사용된다.
- typ : 토큰 유형
alg는 암호화를 하는 것이 아닌, 서명(Signature)을 해싱하기 위한 알고리즘을 지정하는 것이다.
{
"alg" : "SHA256",
"typ" : "JWT"
}
2. 페이로드(Payload)
페이로드에는 토큰에서 사용할 정보의 조각들인 클레임(Claim)이 담겨있다.
{
"jti" : "1000",
"exp" : "14392402",
"https://co-meow.tistory.com" : true,
"username" : "co-meow"
}
Claim은 총 3가지로 나뉘며, Key-Value 형태로 다수의 정보를 담을 수 있다.
(1) Registered Claim (등록된 클레임)
Registered Claim은 토큰 정보를 표현하기 위해 미리 정의된 클레임이다.
다음의 데이터들을 선택적으로 사용할 수 있다.
iss | 토큰 발급자(issuer) |
sub | 토큰 제목(subject) |
aud | 토큰 대상자(audience) |
exp | 토큰 만료 시간(expiration) |
nbf | 토큰 활성 날짜(not before) |
iat | 토큰 발급 시간(issued at) |
jti | JWT 토큰 식별자(JWT id) |
(2) Public Claim (공개 클레임)
Public Claim은 사용자 정의 클레임으로, 공개용 정보를 위해 사용된다. 충돌 방지를 위해 URI 포맷을 사용한다.
(3) Private Claim (비공개 클레임)
Private Claim은 사용자 정의 클레임으로, 서버와 클라이언트 사이에서 임의로 지정한 정보를 저장한다.
3. 서명(Signature)
서명(Signature)은 토큰을 인코딩하거나 유효성 검증을 할 때 사용되는 고유한 암호화 코드이다.
헤더와 페이로드의 값을 Base64 URL-Safe Encode 방식으로 인코딩하고, 인코딩 된 값을 비밀 키를 사용해서 헤더에서 정의한 알고리즘으로 해싱하고, 이 값을 다시 인코딩하여 생성한다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
✔️ JWT 인코딩 / 디코딩
아래 링크를 통해 JWT를 인코딩하거나 디코딩할 수 있다.
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io
출처
긴 글 읽어주셔서 감사합니다 🍀
잘못 작성된 내용은 피드백 주시면 반영하겠습니다 😎