본문 바로가기
강의 및 교육/Inflearn - Webgoat

JWT tokens - 5. JWT signing

by 이우정 2022. 2. 10.
728x90

[ 이론 ]

Authentication and getting a JWT token

token을 얻는 기본적인 순서은 다음과 같다.

(1) username과 pw를 POST하여 서버에 보낸다. (2) 키를 통해 JWT를 생성한다. (3) JWT를 브라우저에 return 한다. (4) 인증 헤더에 JWT를 함께 보낸다. (5) JWT 서명을 확인하고 JWT에서 user 정보를 얻는다. (6) client에게 응답을 전송한다. 

이 흐름에서 서버가 인증 성공을 return하면 username과 pw로 사용자가 로그인하는 것을 볼 수 있다. 서버는 클라이언트에게 새로운 토큰을 만들어서 return한다. 클라이언트가 "인증" 헤더에 새로운 토큰을 부착하여 서버에게 연속적인 호출을 할 수 있다. 서버는 토큰을 읽고 검증한 후 토큰에 있는 정보로 user를 식별한다. 

 

Claims

사용자를 식별하기 위해 토큰은 claims를 포함하고 있고 다른 정보들은 서버가 요청을 충족시키기 위해서 필요하다. 민감한 정보를 토큰에 넣지 않아야하고 항상 안전한 채널을 사용해야 한다. 

 

[ 문제 ]

JWT signing

각각의 JWT 토큰은 클라이언트에게 보내지기 전에 서명되어야만 한다. 만약 서명되지 않는다면 클라이언트 응용 프로그램에서 토큰의 내용을 변경할 수도 있다. 서명에 대한 스펙(설명)은 여기(https://datatracker.ietf.org/doc/html/rfc7515)에서 확인 가능하고, 사용하는 알고리즘에 대한 설명은 여기(https://datatracker.ietf.org/doc/html/rfc7518)에서 확인 가능하다. 알고리즘은 기본적으로 "SHA-2"와 함께 "HAMC"을 사용하거나, "RSASSA-PKCS1-v1_5/ECDSA/RSASSA-PSS"가 사용된다. 

 

Checking the signature

타 작업 수행 전, 서명 검증에 중요한 부분 중 하나인, 검증 전 주의해야할 사항을 알아보자

 

Assignment (도전)

토큰을 변경하여 admin user로 둔갑하고 투표를 초기화하자. 

 

[ 풀이 ]

- https://jdh5202.tistory.com/885

Tom으로 로그인해서 Reset하면 admin만 가능하다고 뜬다. 
버프로 잡은 패킷
해당 access_token 값

JWT는 "."로 구별

버프스위트 Decoder 이용했을때 제대로 디코딩이 안되는걸 볼 수 있음
등호가 생략되어 전송돼 발생하는 문제

본문(Claims)의 내용을 admin으로 위장하도록 변경함. 내용이 변경되면 그에 맞는 별도의 서명이 verify signature 부분에 들어가야함.

claim 값 위장

서버가 서명하기 때문에 verify signature를 만들순 없다.

none을 입력해 서명 없이 보낼 수 있다?

근데 나는 안되더라.. 사람들 보니깐 null이라고 하길래 따라했다. 

header 값 위장

위장한 header와 claim을 "."로 구별하면서 한 문장으로 만든다. 

eyJhbGciOm51bGx9.eyJpYXQiOjE2NDUzMzk1MTEsImFkbWluIjoidHJ1ZSIsInVzZXIiOiJhZG1pbiJ9.

이걸 전송하면 검증된 JWT(admin)로 착각해서 reset 권한을 넘겨받을 수 있다.

 

근데 왜 "none"은 안먹고 null은 먹은걸까? 아는 사람~???


Solution

 

이 과제의 목적은 토큰을 조작하면 서버가 토큰을 다르게 해석할 수 있다는 것이다. JWT 라이브러리가 처음 나타났을 때, 헤더에 지정된 알고리즘을 포함하고 이것과 함께 작업하고자 한다는 것을 의미하는 문자를 명세서에 구현하였다. 

JWT는 암호화 민첩성을 용이하게 하기 위해 "alg" 헤더 파라미터의 형태로 서명 알고리즘의 명시적인 표시를 포함한다. 이로 인해 일부 라이브러리와 응용프로그램의 설계 결함과 함께 여러 공격이 발생하였다. 
- 이 알고리즘은 공격자에 의해 "none"으로 변경될 수 있고, 일부 라이브러리는 이 값을 신뢰하고 어떤 서명도 확인하지 않고 JWT를 "유효화"한다. 
- "RS256" (RSA, 2048bit) 매개변수 값을 "HS256" (HMAC, SHA-256)으로 변경할 수 있고, 일부 라이브러리는 HWAC-SHA256과 RSA 공개키를 HMAC 공개코로 사용하여 서명을 검증하려고 시도한다. (see [McLean] and [CVE-2015-9235]).

기본적으로 라이브러리는 토큰 생성 과정에서 어떤 암호화 작업이 사용되었는지 검증하지 않고 주어진 토큰 대로 구문 분석만 한다. 

 

Solution

 

먼저 "Guest"로 로그인했기 대문에 다른 사용자(ex : Tom)을 선택한다. 사영자 Tom은 투표는 가능하지만 reset할 순 없다. 요청을 보면 응답에 access_token이 반환된다.

GET http://localhost:8080/WebGoat/JWT/votings/login?user=Tom HTTP/1.1

access_token=eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2MDgxMjg1NjYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9t
In0.rTSX6PSXqUoGUvQQDBiqX0re2BSt7s2-X6FPf34Qly9SMpqIUSP8jykedJbjOBNlM3_CTjgk1SvUv48Pz8zIzA

주어진 토큰은 decoding 한다. 

{
  "alg": "HS512"
}
{
  "iat": 1608128566,
  "admin": "false",
  "user": "Tom"
}

admin : false를 변경할 수 있지만 그러면 서명이 무효가 된다. 어떻게 하면 유효한 서명을 받을 수 있을까? RFC 명세서에 alg: none은 유효한 선택이며 안전하지 않은 JWT를 제공한다고 한다. 토큰을 변경시키자. 

headers:

{
  "alg": "none"
}

claims:

{
  "iat": 1608128566,
  "admin": "true",
  "user": "Tom"
}

WebWolf를 사용하면 우리는 토큰을 만들수 있다.

이제 우리는 쿠키 안의 토큰을 교체하고 리셋을 다시 수행 할 수 있다. 주의해야할 것은 마지막에 "."을 추가해야만 유효하다는 것이다. 

 

 

728x90