일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 열 속성
- 재정의
- stream
- 바이너리 카운팅
- DI
- 필드 주입
- hashcode
- docker
- cache
- VUE
- jpa
- KEVISS
- static
- 생성자 주입
- 조합
- lambda
- Test
- redis
- AOP
- MSA
- StringBuilder
- equals
- java
- Spring
- SQL
- jwt
- select_type
- 테스트 코드
- 인덱스
- DDL
- Today
- Total
백엔드 개발자 블로그
JWT 본문
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://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