예전에 장염으로 대형병원 응급실을 갔을 때, 그곳에서 환자 밴드를 손에 채워주고 검사를 시작했습니다.
이 밴드에는 바코드가 있었는데요, 밴드 하나로 개인정보, 병원에 온 이유 등을 말하지 않아도 병원에서는 이사람이 환자임을 알 수 있었습니다.
유효한 밴드를 가지고 있는 것만으로 병원에 온 환자 임이 인증되는거니까요.
웹 서비스에서도 이와같은 원리로 회원을 확인할 수 있는 기능을 하는 것이 있습니다. 바로 JWT 토큰입니다.
JWT 토큰의 정의와 기본 개념
JWT(Json Web Token)는 웹의 사용자 인증을 위해 사용되는 전자 서명된 URL-safe 암호화된 토큰을 의미합니다.
서버에서 특정 방식으로 암호화 처리를 해서 클라이언트에게 토큰을 주면, 클라이언트는 이 토큰을 들고 있다가 필요할 때 서버한테 내밀어서 인가를 요청하고, 서버는 허용하는 것이죠.
앞서 이야기했듯 병원에서 필요한 순간에 바코드가 있는 밴드를 내미는 것처럼요.
URL-safe : 특정 문자들은 URL에서 특별한 의미를 가지기 때문에 사용되지 않음
- 알파벳, 숫자, 일부 특수문자(-),(_),(~),(.) 로만 이루어진 문자
- / , ? , & , = , # , + , % 등이 특별한 의미를 가진 문자에 해당됨
JWT의 구조
이런 JWT는 세 파트로 이루어져 있습니다. 각 파트는 . 으로 구분되어 헤더 / 페이로드 / 시그니처 3가지로 구성됩니다.
흐름을 먼저 간략히 설명하면 payload에는 실제 데이터가 담기고 header와 payload 를 조합하여 암호한 알고리즘으로 signature에 저장합니다.
헤더
토큰의 헤더는 토큰의 타입과 시그니처 생성에 어떤 알고리즘이 사용되었는지 정보를 담고 있습니다.
{
"alg": "HS256",
"typ": "JWT"
}
alg은 알고리즘의 종류를 뜻하며 개인키로 HS256 알고리즘이 적용되어 있음을 뜻합니다.
typ은 타입으로, 토큰의 타입이 JWT인 것을 나타냅니다.
페이로드
{
"sub": "1234567890",
"iss": "graytree",
"iat": 1516239022
}
페이로드에는 사용자 또는 토큰에 대한 property를 key-value 형태로 저장하고 있습니다.
이 각각의 값들을 claim이라 하며 jwt토큰에대한 표준 스펙이 아래와 같이 정의되어 있습니다.
[표준 클레임]
- iss: 토큰 발급자
- sub: 토큰 제목
- aud: 토큰 수신자
- exp: 토큰 만료 시간
- nbf: 토큰 유효화 시간(현재시간보다 nbf가 미래일 경우 토큰은 무효)
- jti: 토큰 고유 식별자(발급자가 여려명일 경우 충돌 방지용)
이외에도 표준스펙에 적힌 클레임은 다양하며, 이 클레임들은 필수사항이 아니라 필요에 따라 적절히 선택하여 넣는 선택사항입니다.
또, 이 클레임 외에도 개발자가 직접 "email": "abc@test.com"과 같은 클레임을 만들어서 넣을수도 있습니다.
시그니처
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
시그니처에는 개발자가 직접 만든 secret 키를 가지고 암호화합니다.
먼저 header을 base64Url 방식으로 암호화하고, 똑같이 payload값도 암호화하여 "."으로 더합니다.
이 값을 HMACSHA256(이하 HS256) 암호화 방식으로 secret 키를 이용하여 암호화합니다.
정리하면, 발급된 jwt 토큰에서 보여지는 header와 payload는 base64Url 방식으로 인코딩되어 있으며, 이 두 개를 더한 값과 secret 값을 이용해서 signature값을 생성하는 것입니다.
실제로 위에 JWT구조에서 만들어진 토큰을 https://jwt.io/ 에서 디코딩 해 볼 수 있습니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiZ3JheXRyZWUiLCJpYXQiOjE1MTYyMzkwMjJ9.xb0NsOkmorTGMPt0Rke-LBcDRuTzX41FzoWIRXBu6oE
이렇게 디코딩 해보면 Header와 Payload는 나오지만 Signature에서 secret 키는 보이지 않습니다.
이부분에 제가 정한 secret 값을 넣고, 다시 토큰을 붙여넣어보면 검증이 된 것을 확인할 수 있습니다.
JWT 주의점과 토큰 조작이 소용없는 이유
앞서 보았다시피 header와 payload는 base64Url방식으로 인코딩 되어있으므로, 매우 쉽게 디코딩이 가능합니다.
그러므로 중간에 토큰이 탈취된다면 header부분과 payload부분을 쉽게 볼 수 있습니다.
그래서 payload에는 토큰에 대한 정보를 담아야 하지만, 중요한 정보 대신 최소한의 정보만 담는 것이 좋습니다.
payload가 쉽게 디코딩된다면, 조작하여 다시 인코딩 하는것도 가능하겠죠.
그렇지만 어떤 secret 값인지 알지 못하기 때문에 signature값이 달라지게 됩니다.
서버측에서 토큰을 검증할때 signature값을 열어서 검증하는데, 이때 secret 값이 다르므로 유효하지 않은 토큰으로 판단해 버립니다.
JWT 장점과 단점
장점
- 서버부하 감소: 별도의 세션을 저장하고 있지 않으므로 서버의 부하가 감소합니다.
- 확장성: 상태를 서버가 가지고 있는 것이 아니므로 stateless하고, 여러 서버 환경에서도 용이합니다.
단점
- 토큰 크기가 세션 ID에 비해 큽니다.
- 토큰을 탈취 시 해당 유저로 접근하는 것을 막기는 어렵습니다.
출처
https://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
'CS' 카테고리의 다른 글
데이터베이스 트랜잭션의 ACID 원칙 (0) | 2024.07.30 |
---|---|
인터넷 프로토콜 스택의 4계층, TCP/IP 레이어 (0) | 2024.07.28 |
남에게 설명할 때 비로소 자신의 지식이 된다.
포스팅이 도움되셨다면 하트❤️ 또는 구독👍🏻 부탁드립니다!! 잘못된 정보가 있다면 댓글로 알려주세요.