JWT(Json Web Token)
JWT가 왜 인증에 사용될 수있을까? 어떻게 사용될까?
JWT란?
JWT(Json Web Token)은 JSON 객체로 인코딩된 클레임 세트를 나타냅니다.
JWT에는 두가지 유형인(JWS, JWE)가 있고, 우리가 흔히 JWT라고 생각하는 방식은 JWS로 더 흔히 사용됩니다. 큰 차이는 JWS는 누구나 값을 보고 사용할 수 있고, JWE는 암호화 되어있어 복호화를 할 수 있는 사용자만 값을 사용할 수 있기 때문입니다.
JWT를 모르는데, JWS, JWE는 또 뭐야! 라고 생각하실 수 있으신데 먼저 JWS의 방식으로 JWT를 이해하고, 더 아래에서 JWS와 JWE를 자세히 이해해봅시다!
JWT의 구성
JWT는 어떻게 생겼는지부터 알아봅시다.
헤더(Header)
헤더에는 토큰 자체에 대한 정보를 담고있습니다. 토큰을 암호화할 때 어떤 알고리즘을 썼는지, 토큰의 타입이 무엇인지 등의 정보를 작성합니다.
JOSE 헤더(JavaScript Object Signing and Encryption) RFC 7519 문서에서는 JWT의 헤더를 이렇게 지칭하고 있습니다. 이 의미는 JWT에 사용된 암호화 및 서명 알고리즘 정보를 담고 있는 JSON 객체를 의미합니다.
페이로드(Payload)
페이로드에는 실제 통신에서 주고 받을 때 사용하기 위한 클레임(실제 정보)이 담겨있습니다. 보통은 담고자 하는 정보가 여러개라, 클레임들(Claims)이 담겨있습니다.
- 클레임(Claim)
- key-value 형태로 된 정보
- 실제 우리가 주고 받을 데이터를 의미합니다.
RFC 7519의 표준에서는 클레임을 3가지 종류로 나눕니다.
- Registered Claim
- 표준화 된 클레임으로, 권장되지만 필수는 아닙니다.
iss
(Issuer): 토큰 발급자sub
(Subject): 토큰 제목 또는 사용자 식별자aud
(Audience): 토큰 대상자exp
(Expiration Time): 만료 시간 (Unix Timestamp)nbf
(Not Before): 토큰 활성 시작 시간iat
(Issued At): 토큰 발급 시간jti
(JWT ID): 토큰 고유 식별자
- Public Claim
- 사용자 정의 클레임으로, 공개용 정보 전달을 위해 사용됩니다.
- Registered Claim 외에는 사용자가 자유롭게 정의할 수 있는데, 이 부분은 다른 여러 애플리케이션이나 서비스의 JWT와 충돌할 수 있습니다. 이를 방지하고자 Public Claim을 작성해 충돌을 방지합니다. RFC 7519에서는 이 내용을 발급하는 곳의 URI로 정의하는 방법을 권장합니다.
1 2 3 4 5 6 7
{ "sub": "1234567890", "name": "John Doe", "email": "john.doe@example.com", "https://www.googleapis.com/auth/userinfo.profile": true, "https://www.googleapis.com/auth/userinfo.email": true }
구글의 Public Claims 예시
- Registered Claim 외에는 사용자가 자유롭게 정의할 수 있는데, 이 부분은 다른 여러 애플리케이션이나 서비스의 JWT와 충돌할 수 있습니다. 이를 방지하고자 Public Claim을 작성해 충돌을 방지합니다. RFC 7519에서는 이 내용을 발급하는 곳의 URI로 정의하는 방법을 권장합니다.
- Private Claim
- JWT 발급자와 수신자 간에 정의된 비공개 클레임으로, 실제 주고 받을 데이터들을 의미합니다.
서명(Signature)
서명은 JWT를 사용했을 때 안전함을 증명하는, 무결성을 보장하는 부분입니다. 서명은 앞에 정의했던 헤더, 페이로드, secret 내용을 합친뒤 암호화 합니다.
- Secret
- 발급하는 곳에서만 알고있는 정보로, 비밀번호와 같은 내용입니다.
서명(Signature): JWT가 안전한 이유
가장 궁금했던 부분입니다. 어떻게 서명을 통해 무결성이 보장되며, 인증 과정에서는 어떻게 서명을 이용해야할지가 가장 궁금했습니다.
검증 과정: 서명의 무결성을 확인하는 법
서버에서 JWT의 무결성은 어떻게 확인할까요?
- JWT의 무결성
- JWT가 변조되지 않았다는 것을 의미
여기서 설명하는 검증 방식은 JWS의 방식입니다. 아래에서 JWS와 JWE에 대해 설명하고 있으니, 먼저 이를 통해 이해하면 좋습니다.
새로운 서명(Header + Payload + secret)을 만들어, 실제 서명과 비교하여 무결성을 확인합니다.
- JWT을
Header
+Payload
+Signature
로 나눕니다.Header
와Payload
,Signatrue
는 점(.
)으로 구분자로 사용해 연결되어있어 이를 통해 나눕니다.
- 새로운 서명(Signature)을 생성합니다.
- 여기서
Header
와Payload
, 서버만 가지고 있던 비밀번호인secret
을 다시 합칩니다. - 암호화 알고리즘을 이용해 암호화합니다.
- 여기서
- 새로운 서명과 보내진 JWT의 서명을 비교합니다.
Payload
의 내용을 변조했다면, 새로운 서명과 기존 서명의 내용이 달라 JWT가 변조된 JWT인 것을 알 수 있습니다.Payload
와Signature
를 변조했다면, 서버가 아니면secret
의 실제 값을 모르기 때문에 제대로 된Signature
를 생성할 수 없어 새로운 서명과 기존 서명의 내용이 달라 JWT가 변조된 JWT인 것을 알 수 있습니다.
JWT와 암호화 방식
JWT을 안전하게 사용하기 위해서 2가지 방식인 단방향(JWS)와 양방향(JWE)을 지원합니다. 이때의 단방향과 양방향은 사용된 암호화 알고리즘의 종류가 단방향인지 양방향인지를 의미하는 것이 아니라, 암호화 알고리즘을 구분할 때와 같이, 검증할 때 복호화를 안하고 검증하는지, 복호화를 이용하는지에 따라 나뉩니다.
알고리즘
먼저 암호화 알고리즘에 대해 간단히 확인하고 넘어가도록 하겠습니다. 이에 대해 잘 알고 계신다면 넘어가셔도 좋습니다.
- 단방향 암호화 알고리즘
- 단방향(암호화)만 가능한 암호화 방식
- 복호화는 절대로 불가능하게 만든다.
- 해시 함수를 이용해 해시 값을 생성하는 방식이다. = 동일한 내용을 암호화하면, 항상 같은 결과가 나온다. = 평문(암호화의 대상, 암호화 이전의 데이터)의 내용이 같다면, 암호문(암호화의 대상, 암호화 이후의 데이터)의 내용도 같다.
- 암호화 된 데이터를 검증하려면, 검증할 평문을 암호화 하여 새로운 암호문을 생성한뒤 원래의 암호문과 비교해 같은지 확인한다.
- 예시 암호화 알고리즘으로는 MD5, SHA256 등이 있다.
- 복호화는 절대로 불가능하게 만든다.
- 양방향 암호화 알고리즘
- 양방향(암호화, 복호화)가 가능한 암호화 방식
- 암호문을 복호화 할 수 있다.
- 대칭키 방식과 비대칭 키 방식 2가지가 있다.
- 대칭키
- 암호화하는 키 = 복호화하는 키
- 하나의 키로 암호화와 복호화를 다 할 수 있기 때문에, 키를 공개하지 않아야 한다.
- 비대칭키(공개 키, 비공개 키)
- 암호화하는 키 != 복호화하는 키
- 2개의 키를 A와 B라고 할 때, A키로 암호화 하면 B키로만 복호화가 가능하고, B키로 암호화하면 A키로만 복호화가 가능하다.
- 2개의 키중 하나는 공개키(공개해도 되는 키), 하나는 비밀키(개인키라고도 함, 보호해야하는 키)로 관리한다.
- 예시 암호화 알고리즘
- 대칭키: DES, AES, SEED, ARIA 등
- 비대칭키: RSA, DSA, ECC 등
- 같은 암호화 알고리즘을 사용한다고 하더라도, 실제 키는 랜덤성을 가지고 있기 때문에 매번 다른 키가 생성된다. 양방향 대칭키 알고리즘인 AES를 이용해서 key1과 key2를 만들고, 같은 평문을 각 키로 암호화하면 다른 결과가 나온다. 그래서 같은 알고리즘을 사용한 키라고 해도, key1로 만든 암호문을 key2로 복호화 할 수 없다.
- 암호문을 복호화 할 수 있다.
이제 진짜 암호화 방식에 따른 JWT를 확인해봅시다.
JWS (JSON Web Signature)
- JWS (JSON Web Signature)
- 서명을 이용해 데이터 변조를 방지하고, JWT의 무결성을 증명합니다.
- 암호화 알고리즘의 종류(단방향/양방향)에 관계없이 사용할 수 있습니다.
- 중요 포인트는, 서명을 새로 만들고 JWT 의 서명과 비교하는 방식으로 검증한다는 것입니다. = 서명을 역으로 해독할 수 없다는 의미로 단방향 이라고 합니다.
- 암호화 알고리즘의 종류(단방향/양방향)에 관계없이 사용할 수 있습니다.
쉽게 말해 위에서 검증을 설명할 때의 방식을 JWS라고 이해하시면 됩니다. 그런데 이때 JWS를 단방향이라고 하는 이유는, (위에서 설명한 것과 같이) 검증할 때 암호화 했던 Signature
를 복화해서 사용하는 것이 아니라, 검증할 JWT의 Header, Payload를 이용해 새로운 Signature
를 만들어 검증할 JWT의 Signature
와 비교한다는 점에서 단방향이라고 합니다.
단방향 알고리즘을 이용해 비밀번호를 검증 할 때 복호화해서 비교하지 않고 검증 대상의 데이터를 다시 암호화해 비교하는데, 이때의 단방향을 의미한다고 생각하시면 됩니다.
암호화 알고리즘의 차이
단방향 암호화 알고리즘과 양방향 암호화 알고리즘은 분명 암호화 알고리즘의 종류가 달라 차이가 생길텐데, 어떤 차이가 있는걸까요?
- 단방향 암호화 알고리즘
- 단방향 암호화 알고리즘을 이용해 암호화했기 때문에, 암호화한
Signature
는 그 누구도 복호화할 수 없지만, 알아 내기 위해secret
에 대한 내용을 변경하며 동일한 암호화 알고리즘을 통해 만들고 이 결과가 실제Signature
와 같은지 비교하는 방식으로는 확인할 수 있다.자물쇠의 4자리 비밀번호에 대해서, 0000~9999까지 다 입력해서 비밀번호를 알아내는 방식과 같다고 생각해주시면 됩니다.
- 다만, 그 계산을 다 해서
Signature
를 알아내기전에, 토큰의 유효시간이 끝난다. 그렇지만,secret
의 값이 짧거나 복잡성이 낮아 이러한 계산이 빠르게 된다면 위험할 수 있다. - 다만, 그 계산을 다 해서
- 서명: 헤더 + 페이로드 +
secret
을 직렬화 -> 단방향 암호화 알고리즘을 이용해 서명 생성 - 인증: 새로 서명 데이터를 만들어, 인증할 대상의 서명과 비교
- 양방향 암호화 대칭키 알고리즘
- 실제 암호화/복호화에 사용되는 키는 서버만 가지고 있으므로, 같은 알고리즘이더라도 같은 키가 아니면 복호화 할 수 없다.
- 서버에서도 복호화할 수 있지만, 복호화 하지않고 단방향 방식(=서명을 다시 생성하고 암호화해 결과를 비교하는 방식)으로 검증한다.
- 서명: 헤더 + 페이로드 +
secret
을 직렬화 -> 단방향 암호화 알고리즘을 이용해 서명 생성 - 인증: 새로 서명 데이터를 만들어, 인증할 대상의 서명과 비교
- 양방향 암호화 비대칭키 알고리즘
- 비밀키는 서명을 생성하는 곳에서 가지고 있고, 공개키는 서명을 검증하는 곳에서 가지고 있습니다. 두 곳이 한 곳인 경우엔 한 곳에서 가지고 있습니다.
secret
을 사용하지 않고, 서명을 생성합니다.
- 서명: 헤더 + 페이로드를 직렬화 -> 비밀키를 이용해 서명을 생성
- 인증: 기존 서명을 공개키롤 복호화가 되는지 확인해 인증(복호화 한 값을 사용하는 것이 아니라, 복호화가 되는지!를 확인)
암호화와 서명의 차이 암호화와 서명은 서로 비슷하지만 다른 의미를 가집니다.
- 암호화
- 암호화의 대상을 남들이 보지 못하도록 보호하는 것을 목적으로 합니다.
- 서명
- 서명의 대상의 무결성을 보장하기 위한 수단입니다.
- 서명의 방법으로 암호화(주로 비대칭키 방식)을 이용합니다.
JWE (JSON Web Encryption)
- JWE (JSON Web Encryption)
- 암호화되고, 무결성이 보호되는 메시지 입니다.
- 양방향(대칭키/비대칭키) 암호화를 사용합니다. 메시지의 내용을 암호화해 보호하고, 실제 사용될 때에는 복호화를 해 사용합니다.
- 누구나 값을 볼 수 있는 JWS와 달리, 암호화 되어있어 데이터의 기밀성까지 보장합니다.
- 양방향(대칭키/비대칭키) 암호화를 사용합니다. 메시지의 내용을 암호화해 보호하고, 실제 사용될 때에는 복호화를 해 사용합니다.
JWE의 구성과 생성
JWE의 방식은 JWS와는 조금 달라, JWE의 구성을 살펴보겠습니다.
- 헤더(Protected Header)
- 암호화 알고리즘 등의 메타데이터를 포함
- 암호화된 키(Encrypted Key)
- CEK(Content Encryption Key, CEK): 실제 사용할 값인 클레임들을 암호문으로 암호화할 때 사용되는 키
- CEK를 암호화 한 것
- 주로 CEK는 AEAD 종류의 알고리즘 중 하나를 사용합니다.
- 초기화 벡터(Initialization Vector)
- 암호화 작업에 사용되는 값
- 암호문(Ciphertext)
- 실제 암호화된 콘텐츠
- 실제 사용할 값인 클레임들을 암호화 알고리즘으로 암호화 한 내용
- 인증 태그(Authentication Tag)
- 암호화된 콘텐츠의 무결성을 검증하는 데이터
JWE에서는 위 5개의 항목을 직렬화 한 구조를 가지고 있습니다.
JWE의 직렬화 방법 JWS는 하나의 직렬화 방법을 사용했는데, JWE에서는 직렬화 방법을 2가지를 지원합니다.
- 컴팩트 직렬화(Compact Serialization): 다섯 부분을 점(
.
)으로 구분한 형식, JWS에서 사용한 방법- JSON 직렬화(JSON Serialization): 모든 부분을 JSON 객체로 표현하는 형식
각 구성 항목을 자세히 살펴보며, 어떻게 JWE가 생성되는지 알아봅시다.
JOSE 헤더(Protected Header)
JSW의 헤더와 같이, 토큰의 메타 데이터를 포함합니다.
암호화된 키(Encrypted Key)
실제 사용할 값인 클레임들이 있는 Payload에 해당되는 내용을 암호화 하는 키를 의미합니다. 보통 이 키의 암호화 알고리즘은 AEAD 종류의 암호화 알고리즘을 사용하는데, AEAD 타입의 암호화 알고리즘을 이용하면 Payload를 평문에서 암호문으로 암호화하는 과정에서 인증 태그가 자동으로 생성됩니다. JWS의 서명과 같이, 인증 태그를 이용해 이 토큰의 무결성을 보장하기 때문입니다.
만약 AEAD 타입의 암호화 알고리즘을 사용하지 않는다면, 무결성 검증에 대해 따로 추가하여 진행할 수 있습니다.
초기화 벡터(Initialization Vector)
이는 같은 키를 이용해 같은 평문을 암호화하면, 암호문도 동일하게 나오는 대칭키 암호화 알고리즘의 특성으로 인해 CEK를 이용해 Payload를 암호화 할 때, 평문이 동일한 데이터라도 암호문이 다르게 나오게 하기 위한 값입니다.
그렇게 하지 않으면, 암호화된 데이터의 패턴을 통해 암호화된 데이터를 예측할 수 있기 때문입니다.
CEK에서 주로 사용되는 AEAD 타입의 알고리즘은 대칭키 암호화 방식입니다.
암호문(Ciphertext)
JWS에서 Payload와 같은 값으로, 실제 주고 받을 데이터합니다. 이 데이터를 CEK를 이용해 암호화한 암호문으로 포함합니다.
인증 태그(Authentication Tag)
CEK를 이용해 암호문을 생성하면, 보통 CEK는 AEAD 종류를 사용하기 때문에 인증 태그가 생성됩니다. 인증 태그는 JWS에서 서명과 같은 역할을 하는데, 데이터의 무결성을 보장합니다.
만약, CEK를 AEAD 종류의 알고리즘을 사용하지 않아 인증 태그가 생성되지 않는다면, 무결성을 검증하기 위해 JWS에서 사용했던 방식을 이용할 수 도 있고, 인증 코드 방식으로도 할 수 있습니다.
JWE의 인증
그러면 JWE를 이용한 인증은 어떻게 동작할까요?
동작과정을 살펴보기전에, 사전에 준비를 해야합니다. 실제 데이터는 암호화 되어있기 때문에, 토큰을 받는 곳과 보내는 곳에서는 미리 암호화 키를 나눠가집니다. 비대칭 키라면 개인키와 공개키를 나눠가지고, 대칭키라면 동일한 비밀키를 나눠가지게 됩니다.
- JWE 파싱: 5개의 항목으로 파싱합니다.
- CEK를 사전에 나눠가진 키를 통해 복호화합니다.
- 인증 태그를 통해 무결성음 검증합니다. AEAD 종류의 알고리즘이 아니라면, JWS와 같이 서명을 이용하거나, 사전의 정의된 인증 코드를 암호화해 보내고, 받은 곳에서도 미리 알고 있는 인증코드로 다시 암호화해 이를 비교합니다.
- 복호화된 CEK와 IV를 이용해 암호문을 복호화합니다.
왜 JWE는 이중 암호화를 할까? JWE를 보면 이중으로 암호화를 하고 있습니다.
- 실제 내용을 CEK로 암호화
- CEK를 암호화
왜 이렇게 이중으로 암호화를 할까요?
- 키 관리의 유연성
- 사전에 미리 공유된 키가 아닌, 암호화 시마다 생성해서 사용하는 CEK를 이용함으로써, 같은 암호화를 진행해도 매번 다르게 암호문이 생성되어 재생 공격(replay attack)을 막습니다.
- 키 교체의 어려움
- 보안의 모범 사례는 키를 자주 교체하는 것인데, 단일 키로하게 되면 실제로 나눠 갖고 있는 키를 계속 변경해야합니다. 그렇게 되면 키를 자주 교체하기가 어려운데, 실제 나눠 갖는 키는 고정하되 그 내에 키는 계속 변경되게 함으로 써 이를 쉽게 합니다.
집의 도어락 비밀번호가 고정되어있으면 유출될 가능성이 높은데, 이때 비밀번호를 자주 교체함으로써 보안성을 높이는 것과 같은 맥락입니다.
비교
JWS와 JWE
보안적인 측면을 보자면 당연히 데이터를 암호화해서 보내는 JWE가 더 안전합니다. 그렇지만, 데이터를 클라이언트에서도 사용해야한다면, 오히려 JWS가 더 편리할 수 있습니다.
주고 받아야하는 데이터가 무결성만 보장되면 될지, 아니면 기밀성도 보장 되어야 할지와 같은 보안과 실제 주고 받을 때에 어떤 것이 더 편리할 것인가 등을 고려해 선택해야한다고 생각합니다.