JWT 개념 잡기
1. Browser에서 인증 Server로 인증정보(username,password) 보낸다
2. 인증서버에서는 사용자를 식별하고 password가 일치하면 JWT(Json Web Token) 발행한다.
3. 인증 받은 하고 Broswer애 리턴 한다.
4. Browser는 JWT 인증 토큰을 헤더에 담아 Server에 보낸다.
4. Server에서는 JWT 서명값을 검증하고, JWT 토큰에서 사용자를 식별 한다.
JWT의 주요 목적은 두 관계자 사이에 안전하게 claim을 전송하는 것이다.
claim이란 특정 관계자나 오브젝트에 대한 definition 혹은 assertions이다.
다음은 signature가 들어간 claim의 예시이다.
헤더
{
"alg": "HS256",
"typ": "JWT"
}
본문
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
서명
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
최종
목적을 달성하기 위해 JWT는 몇가지 claim을 표준화했고 JWS(JSON Web Signature)로 signing하거나 JWE(JSON Web Encryption)으로 암호화할 수 있도록 했다.
모든 JWT는 header, payload 그리고 signature/encryption data로 총 3가지로 구성되어 있다.
처음 두 속성은 JSON 오브젝트이고 세번째 속성은 signing이나 encryption 알고리즘에 따라서 다르다. 그리고 암호화되지 않는 JWT의 경우에는 생략된다.
1. The Header
각 JWT는 claim이 있는 header를 가지고 있다. 이 claim은 어떤 알고리즘이 사용됐는지, signed 혹은 encrypted인지, 그리고 나머지 부분의 JWT를 어떻게 파싱해야하는지 선언한다.
암호화되지 않는 JWT header의 필수 claim은 alg claim뿐이다. alg claim은 해당 JWT를 signing and/or decrypting하는 데 사용하는 알고리즘이다. 암호화되지 않는 JWT는 이 claim을 반드시 none으로 설정해야한다.
2. The Payload
필수적인 claim은 없지만 특정 claim은 정해진 의미를 가지고 이런 claim을 registered claim이라고 한다. registered claim으로는 iss, sub, aud, exp, nbf, iat, jti가 있다. 모든 이름이 짧은데, 이건 JWT를 최대한 작게 만드려는 디자인 원칙에 맞춘 것이다.
3. JSON Web Signature(JWS)/JSON Web Encryption(JWE)
JSON Web Signature(JWS)
signature의 목적은 관계자들이 JWT을 검증하기 위한 것이다. 여기서 검증이란 JWT가 담고 있는 데이터가 변조되지 않은 것을 확인하는 것이다. 그러므로 어떤 관계자든지 JWT가 제공하는 정보로 signature 검사를 할 수 있다. 그러나 signature는 다른 관계자가 JWT의 데이터를 읽는 것을 막진 못한다. 암호화가 그 역할을 하게 된다.
가장 흔한 공격은 JWT의 signature를 변조하는 것이다. 그러니 JWT가 각자의 요구사항에 맞게 검증되게 해야한다.
JSON Web Encryption(JWE)
JWE는 제 3자들에게 데이터가 노출되지 않도록 한다.
JWS와 JWE는 비슷해보이지만 차이가 있다. encryption과 validation은 데이터를 읽지 못하게 하는 것을 제외하면 비슷해 보이지만 그렇지 않은 경우가 있다. 이 점을 이해하기 위해서 JWS, JWE의 2가지, shared secret scheme, public/private-key scheme을 아는 게 중요하다.
shared secret scheme은 모든 관계자가 shared secret을 가지기 때문에 가능하다. shared secret을 가진 각 관계자는 encrypt/decrypt가 가능하다. 이건 JWS의 경우와 유사하다. secret을 가진 관계자는 검증과 signed token 생성을 할 수 있다.
그러나 public/private-key scheme은 다르게 동작한다. JWS에서는 private key를 가진 관계자가 token을 sign, verify할 수 있고 public key를 가진 관계자는 verify만 할 수 있다. 그런데 JWE에서는 public key 소유자들은 데이터를 encrypt할 수 있고 private key 소유자들만이 decrypt/encrypt 할 수 있다.
정리하자면 JWE는 JWS와 같은 식으로 동작하지 않으므로 JWS의 역할을 할 수 없다. public/private key scheme을 사용할 땐 JWS와 JWE는 상호 보완적이다.
Practical Applications
1.Client-side/Stateless Sessions
세션을 인증하고 보호하기 위해 signing하고 필요하다면 암호화한다. 클라이언트의 데이터는 변조 위험이 높으므로 백엔드는 조심해서 다루어야 한다. signature는 변조 위험에 대응하기 위해 유효성을 검사하는데 사용한다. 암호화는 제 3자로부터 데이터를 읽지 못하기 위해 사용한다.
1.1 Security Considerations
1.1.1 Signature Stripping
JWT에 대한 흔한 공격 방법은 signature를 제거하는 것이다. 검증을 잘 못한다면 JWT를 unsigned 상태로 된 token을 유효한 token으로 받아들이게 될 수도 있다. 공격자에 의해 payload가 변조된 채로 말이다. 이 공격은 간단하게 방어할 수 있는데, 애플리케이션에서 unsigned JWT를 유효하지 않는 token으로 받아들이도록 하면 된다.
1.1.2 Cross-Site Request Forgery(CSRF)
CSRF 공격은 유저가 로그인한 사이트를 대상으로 다른 사이트에서 요청을 보내는 것이다. 이를 위해 공격자 사이트에는 타켓에 대한 URL을 포함하고 있어야 한다. 흔한 예시는 타겟을 가리키는 src가 포함된
tag가 embed되있는 것이다.
타겟사이트에 유저가 로그인해 있고 그 사이트가 세션을 저장하는데 쿠키를 쓴다면 쿠키가 요청과 함께 보내질 것이다. CSRF 방어 코드가 없다면 이 요청을 유효한 요청으로 받아드려진다. JWT 또한 쿠키에 저장될 수 있다.
short-lived JWT가 이 경우 도움이 된다. 보통의 방어 방법은 origin 검증을 하는 것이다. JWT가 쿠키에 저장되있지 않다면 CSRF 공격은 불가능하다.
1.1.3 Cross-Site Scripting(XSS)
XSS 공격은 신뢰하는 사이트에 JavaScript 코드를 집어넣는 것이다. 그 코드는 쿠키에서 token을 훔친다. access token이 만기전에 유출됐다면 악의적인 유저가 리소스에 접근하는 용도로 사용할 수 있다.
보통의 XSS 공격은 백엔드의 잘못된 유효 검증때문에 일어난다. 예를 들면, 유저가 코멘트를 달았을 때 그 데이터는 백엔드에 저장되고 코멘트를 보는 다른 유저들에게 전시된다. 백엔드가 코멘트를 적절하게 sanitize하지 않았다면 tag 같은 코드가 들어갈 수 있다. 이 코드로 악의적인 유저가 쿠키에 저장된 인증 정보를 훔쳐갈 수 있다.
방어 방법은 백엔드로 전송된 모든 데이터에 대해 적절한 검증을 하는 것이다. 특히 클라이언트에서 보낸 데이터는 반드시 sanitize해야 한다. 쿠키에 대해서는 HttpOnly flag를 설정해 JavaScript 코드로 접근하는 것을 방어하는 것이 가능하다.
2.1.2 Are Client-Side Sessions Useful?
pros: backend에서 database lookup 등의 작업이 없다
cons: JWTs가 너무 큰 경우 bandwidth에 영향을 줄 수 있다.
애플리케이션에 따라서 client-side data와 backend의 database lookup 사이의 균형이 필요하다.
JWT 공식사이트의 Sebastián E. Peyrott, Auth0 Inc. “The JWT Handbook.”을 요약 정리했다. 더 자세한 내용은 사이트를 참고바란다. 이 글의 예시 외에도 Federated Identity, OAuth Example, 사용한 알고리즘들이 정리되어 있다.