[ 이론 ]
Authentication and getting a JWT token
token을 얻는 기본적인 순서은 다음과 같다.
이 흐름에서 서버가 인증 성공을 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
JWT는 "."로 구별
본문(Claims)의 내용을 admin으로 위장하도록 변경함. 내용이 변경되면 그에 맞는 별도의 서명이 verify signature 부분에 들어가야함.
서버가 서명하기 때문에 verify signature를 만들순 없다.
none을 입력해 서명 없이 보낼 수 있다?
근데 나는 안되더라.. 사람들 보니깐 null이라고 하길래 따라했다.
위장한 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를 사용하면 우리는 토큰을 만들수 있다.
이제 우리는 쿠키 안의 토큰을 교체하고 리셋을 다시 수행 할 수 있다. 주의해야할 것은 마지막에 "."을 추가해야만 유효하다는 것이다.
'강의 및 교육 > Inflearn - Webgoat' 카테고리의 다른 글
JWT tokens - 8. JWT cracking (0) | 2022.02.11 |
---|---|
JWT tokens - 7. Code review (0) | 2022.02.11 |
JWT tokens - 3. Decoding a JWT token (0) | 2022.02.10 |
Authentication Bypasses - 2. 2FA Password Reset (0) | 2022.02.09 |
SQL Injection (mitigation) - 7. Zip Slip assignment (0) | 2022.02.09 |