백엔드 개발자 블로그

JWT 본문

ETC/웹 취약점

JWT

backend-dev 2024. 3. 29. 21:18

JWT에 대해 설명하고 JWT 취약점을 발견 및 대응하기 위해 작성되었습니다.

 

JWT 구조

[Base64(헤더)].[Base64(페이로드)].[서명]

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Illlc29uZyBMZWUiLCJpYXQiOjE1NzQ3NTk2OTZ9. NHbhguwzhrt0wDhT26rIMfhyCM6oIevtxDSvAC_0N9A

헤더(Header)

{

"typ" : "JWT",
"alg" : "HS256"

}

페이로드(Payload)

{

"sub" : "1234567890",
"name" : "kep",
"iat" : 1574759696

}

서명(Signature)

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), KEY)


JWT 보안 고려사항

토큰 내 중요정보 노출

JWT의 페이로드는 Base64로 인코딩되어 있어 누구나 쉽게 디코딩 가능함

  • 페이로드를 디코딩하여 중요정보(개인정보, 세션을 의미하는 고유 데이터 등)가 존재하는지 확인

 

토큰 만료 시간 설정

토큰 만료 시간은 반드시 설정해야하며, 가능한 한 짧게 설정해야 함

  • 개발 담당자 인터뷰 및 테스트를 통해 설정된 만료 시간을 확인

 

서버 측의 서명 검증 여부 확인

헤더와 페이로드의 무결성 검증을 위해 백엔드 코드에서 서명 검증이 정상적으로 이루어져야 함

  • 헤더와 페이로드만 변조하여 토큰을 사용할 수 있는지 확인

 

none 해싱 알고리즘을 이용한 토큰 변조

JWT를 생성하는 일부 라이브러리는 none 알고리즘으로 서명된 토큰을 유효한 토큰으로 취급함

  • JWT 헤더의 해싱 알고리즘을 none으로 설정하여, 인증 우회와 페이로드 변조가 가능한지 확인

1) 헤더의 알고리즘을 none으로 설정 후 Base64 인코딩

{"typ":"JWT", "alg":"none"}

2) 페이로드를 원하는대로 수정 후 Base64 인코딩

3) 서명은 작성하지 않음

eyJhbGciOiJub25lIn0 .eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Illlc29uZyBMZWUiLCJpYXQiOjE1NzQ3NTk2OTZ9 .

 

공개키 암호를 대칭키 암호로 수정 (RS256 → HS256)

  • RS256 - 개인키를 사용하여 서명, 공개키를 사용하여 검증
  • HS256 - 비밀키를 사용하여 서명, 검증
  • 헤더의 알고리즘을 RS256에서 HS256으로 변경하면, 일부 라이브러리에서는 공개키를 비밀키로 인식하여 HS256 알고리즘을 사용하여 서명을 확인함
  • 클라이언트 측에서 공개키를 획득할 수 있다면, 헤더의 알고리즘을 HS256으로 수정 및 페이로드 변조 후 공개키로 서명하여 토큰 사용이 가능한지 확인

1) 헤더의 알고리즘을 RS256 → HS256으로 수정 후 Base64 인코딩

{"typ":"JWT", "alg":"RS256"}  {"typ":"JWT", "alg":"HS256"}

2) 페이로드를 원하는대로 수정 후 Base64 인코딩

3) 획득한 공개키로 서명

 

HS256 대칭키 브루트포스 공격

HS256 알고리즘은 서명에서 사용하는 키 강도가 약할 경우 브루트포스 공격으로 키 값을 획득 가능

  • 브루트 포스 공격을 시도해 키 값을 쉽게 획득할 수 있는지 확인 (jwtbrute, jwtcat, PyJWT, 존더리퍼 등 활용)

[키 길이 설정]

rfc7518에서는 해시 출력과 같은 크기 이상인 길이의 키를 사용하라고 권고함 (HS256 의경우 256bits, 아스키 32문자 이상)

 

써드파티 인증 시 토큰 이슈 (Substitution Attacks)

써드파티 인증을 위해 토큰 사용 시 어떤 서비스의 토큰인지 검증하지 않을 경우, A 서비스를 위한 토큰을 B 서비스에 요청하면 인증 가능함

  • A 서비스 토큰을 B 서비스에서 사용 가능한지 확인

 

토큰의 권한 체크 이슈 (Cross-JWT Confusion)

토큰 사용 시 어떤 권한이 허용된 토큰인지 검증하지 않을 경우, 하나의 목적으로 발행된 토큰이 다른 용도로 사용될 수 있음

  • 토큰 사용 시 허용된 권한만 이용 가능한지 확인

[예시]

아래는 joe 사용자에게 user-database의 write권한을 부여한 토큰

서버 측에서 aud를 정확하게 검증하지 않고 cool-company 문자열이 있는지만 확인할 경우, cool-company의 item-database(다른 DB)에서 write 권한을 사용할 수 있음

{

"sub" : "joe",
"perms" : "write",
"aud" : "cool-company/user-database",
"iss" : "cool-company"

}

 

토큰이 정상적으로 폐기되는지 확인

사용자가 토큰 만료 시간 이전에 토큰의 폐기를 원할 경우(로그아웃, 토큰이 탈취된 경우 등), JWT는 명시적인 토큰 폐기 기능이 없으므로 별도로 구현해야 함

  • 토큰 폐기 기능이 존재한다면, 정상적으로 구현되었는지 확인을 위해 폐기 후 토큰을 사용할 수 있는지 확인한다.

[구현 방법]

세션 인증 방식의 로그아웃 기능을 모방하여, 폐기된 토큰 목록을 만료시각까지 블랙리스트 형식으로 서버에서 갖고 있도록 구현 (유효한 토큰인지 검증 시 반드시 만료시간보다 블랙리스트가 우선 시 되어야 함)

 

 

Ref

https://jwt.io/

https://tools.ietf.org/html/rfc7519

https://datatracker.ietf.org/doc/draft-ietf-oauth-jwt-bcp/

https://auth0.com/blog/a-look-at-the-latest-draft-for-jwt-bcp/

https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet_for_Java.html