[JWT] 1장. JWT(JSON Web Token)란 무엇인가?
작성자 - S1ON개요
웹 진단 업무를 수행하며 모던 웹에서 JWT라는 토큰을 본 적이 있다. 그 때는 이게 뭔가 하면서 리퀘스트 헤더에 있는 JWT 값을 대충 디코딩해보고 대충 값 변조해서 재인코딩 후 서버로 보냈었고 특별한게 없어서 넘어갔었다. 하지만 최근에 모든 껀덕지를 활용해서 성과를 내야하는 프로젝트를 수행했고, 해당 웹에서는 세션이 아닌 JWT를 통해 사용자 인증이 이루어졌고 좀 더 자세히 알아야 할 필요를 느껴 정리를 시작했다.
JWT 란?
JWT는 JSON Web Token의 약자로 전자 서명된 URL-safe(URL로 이용할 수 있는 문자로 구성된)의 JSON으로 유저를 인증하고 식별하기 위한 토큰(Token) 기반 인증이며, RFC 7519를 통해 자세한 명세를 확인할 수 있다.
웹에서 사용자 인증 정보를 클라이언트에 보관하는 쿠키 방식이 보안에 취약하다는 이유로 서버 측에 인증 정보를 보관하는 점점 세션을 권장 및 사용하는 형태로 변경되었지만 JWT는 다시 인증 정보를 토큰에 심고 클라이언트에 보관하는 방식을 채택했다.
JWT는 JSON 데이터를 Base64 URL-safe Encode를 통해 인코딩하여 직렬화한 것이 포함되며, 토큰 내부에는 위변조 방지를 위해 개인키를 이용한 전자서명도 있다. 따라서 사용자가 JWT를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며, 검증이 완료되면 요청한 응답을 돌려준다.
※ Base64 URL-safe Encoed는 일반적인 Base64 Encode를 URL에서 오류없이 사용하도록 +, / 를 각각 -, _ 로 표현한다.
JWT 인증 순서
일반적으로 JWT를 사용하는 경우 아래와 같은 순서로 진행된다.
1) 클라이언트 사용자가 아이디, 패스워드를 통해 웹 서비스 인증
2) 서버에서 서명된(Signed) JWT를 생성하여 클라이언트에 응답으로 회신
3) 클라이언트가 서버에 데이터를 추가적으로 요구할 때 JWT를 HTTP Header 또는 URL 파라미터로 첨부
4) 서버에서 클라이언트로부터 온 JWT를 검증
JWT 구조
JWT는 아래와 같이 . 을 구분자로 3가지(헤더, 내용, 서명)로 이루어져 있다.
첫 번째, 헤더(header)는 두 가지의 정보를 가지고 있다.
- typ: 토큰의 타입(JWT)을 지정한다.
- alg: 해싱 알고리즘 정보를 지정한다. 해싱 알고리즘은 보통 HMAC SHA256 혹은 RSA가 사용되며,
토큰 검증 시 signature 부분에서 사용된다.
아래 예제에서는 HMAC SHA256이 해싱 알고리즘으로 사용되는데 이 값을 Base64 URL-safe Encode로 인코딩 해보자.
{ "typ": "JWT", "alg": "HS256" } |
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 |
두 번째, 내용(payload)에는 토큰에 담을 정보가 들어간다. 정보의 한 조각을 클레임이라고 부르며, name/value의 한 쌍으로 이뤄져 있고 페이로드는 여러 개의 클레임으로 구성할 수 있다. 클레임은 크게 등록된(registered) 클레임, 공개(public) 클레임, 비공개(private) 클레임 세 가지로 구분된다.
1. 등록된(registered) 클레임
1) iss: 토큰 발급자 (issuer)
2) sub: 토큰 제목 (subject)
3) aud: 토큰 대상자 (audience)
4) exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식이며, 현재 시간 이후로 설정되어야 한다.
5) nbf: Not Before 를 의미하며, 토큰의 활성 날짜이다. NumericDate로 지정하며, 해당 일까지 토큰이 처리되지 않는다.
6) iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있다.
7) jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용된다. 일회용 토큰에 사용하면 유용하다.
2. 공개(public) 클레임
공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 한다. 충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 짓는다.
3. 비공개(private) 클레임
등록된 클레임도아니고, 공개된 클레임들도 아니며, 양 측간에 (보통 클라이언트 <->서버) 협의하에 사용되는 클레임 이름들이다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할 때에 유의해야 한다.
아래 예제의 내용(payload)는 2개의 등록된 클레임, 1개의 공개 클레임, 2개의 비공개 클레임으로 구성되어 있다.
{ "iss": "velopert.com", "exp": "1485270000000", "https://velopert.com/jwt_claims/is_admin": true, "userId": "11028373727102", "username": "velopert" } |
eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0 |
세 번째, 서명(signature)은 헤더와 내용을 비밀키 해시로 생성한 값이다. 서명을 만드는 슈도코드(pseudocode)의 구조는 다음과 같다. 해당 방식으로 생성된 해시는 base64 형태로 나타내면 된다.
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) |
WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w |
JWT 검증
3개의 예제를 통해 JWT를 구성하는 헤더,내용,서명으로 토큰을 구성하고 jwt.io 사이트를 이용하여 검증해보자.
결론
다음 장에서는 사용자 인증 정보가 담겨 있는 JWT 토큰을 탈취하는 방법과, 탈취한 JWT 토큰을 변조하기 위해 필요한 secret key를 추출하는 방법을 연구하여 모의해킹에 어떻게 활용할 수 있는지 확인해보자.
참고사이트
'Season 1 > 기술 보안' 카테고리의 다른 글
Nmap NSE (0) | 2022.07.31 |
---|---|
Shodan(feat. 포트 스캔) (0) | 2022.07.31 |
IE 취약점이 Edge에서 동작할까? (0) | 2022.07.31 |
[AWS Practitioner 자격증] 09. AWS Access 방식 (0) | 2022.07.18 |
[AWS Practitioner 자격증] 08. IAM MFA 개요 (0) | 2022.07.18 |