JWT: 구조 및 취약점
1) JWT는 무엇이며 어디에서 사용됩니까?
JWT는 'Base64Url (헤더) 형식의 컴팩트 한 독립형 클레임 컨테이너입니다. Base64Url (페이로드). Base64Url (서명) '.
다음에 사용됩니다
JWS (서명 된 토큰-진위/무결성),
JWE (암호화 된 토큰-개인 정보 보호),
서비스 간 인증뿐만 아니라 액세스/ID 토큰으로서의 OIDC/OAuth2.
장점: 자율성, 충격성, 낮은 오버 헤드. 단점: 잘못된 검증, 복잡한 리콜 사례의 위험.
2) JWT 구조
2. 1 헤더 (JSON)
최소: 알고리즘 및 키 식별자.
json
{ "alg": "ES256", "kid": "jwt-2025-10", "typ": "JWT" }
'alg': 서명/암호화 알고리즘 (RS256/ES256/PS256/HS256 등).
'kid': 키 포인터 (JWKS 회전 용).
선택적 핵심 소스: 'jku', 'x5u' (취약점 § 6 참조) 3).
2. 2 페이로드 (JSON)
표준화 된 우표:- 'iss' (발행자), 'aud' (청중), 'sub' (주제)
- 'exp' (만료 시간), 'nbf' (이전이 아님), 'iat' (발행)
- 'jti' (토큰 ID, 취소 가능)
- 도메인 스탬프: '스코프/역할', '테넌트', 'kyc _ 레벨' 등
2. 3 서명
JWS = 'sign (base64url (헤더) + "." + base64url (페이로드), private _ key)'
검증: 엄격하게 대응하는 공개 키 및 서버가 기대하는 알고리즘.
3) 기본 수표 불변
1. 알고리즘은 리소스 서버의 구성 (허용 목록) 에 의해 수정되며 '헤더의 내용으로 신뢰할 수 없습니다. alg '.
2. 'iss' 와 'aud' 를 확인하여 정확한 'exp/nbf' 를 확인하십시오-작은 'clock _ skew' (λ30-60) 를 고려하십시오.
3. 하나의 키가 있고 회전이없는 경우에만 '키드' 가없는 토큰을 거부하십시오. 그렇지 않으면 '아이' 를 요구하십시오.
4. 객체 수준 승인 없이는 스탬프를 신뢰하지 마십시오 (BOLA 우선).
5. 파싱 - 암호화 검증 후; 디코딩 전에 기본 크기를 확인합니다.
4) JWS vs JWE
JWS: 서명했지만 읽을 수 있습니다. 페이로드 PII/비밀을 입력하지 마십시오.
JWE: 페이로드를 암호화합니다. 통합이 더 어렵고 핵심 모델이 중요합니다.
대부분의 API에서 페이로드의 민감한 데이터에 대한 JWS + 금지로 충분합니다.
5) 토큰 수명주기
액세스: 단기 (5-30 분).
새로 고침: 더 긴 (7-30 일), 사용 중 회전 (한 번), "블랙리스트" 'jti/sid' 유지
취소: 'jti' with TTL, 불투명 토큰에 대한 내성, 사건에 대한 약어 'exp' 를 나열합니다.
키 회전: 겹치는 JWKS (이전 + 새) 는 "키 회전" 기사를 참조하십시오.
6) 빈번한 취약성 및 폐쇄 방법
6. 1 'alg = none '/알고리즘 대체
결론: 서버는 'alg' 필드를 신뢰하고 서명되지 않은 토큰을 허용합니다.
보호: 서버의 하드 허용 알고리즘 목록; '없음' 과 예기치 않은 값을 거부합니다.
6. 2 RS256 → HS256 교환 (대칭)
결론: 공격자는 'alg' 를 HS256으로 대체하고 공개 키를 HMAC 비밀로 사용합니다.
보호: 구성을 통해 알고리즘에 키를 바인딩합니다. 하나의 '키드' 에 대칭/비대칭 공급자를 혼합하지 마십시오.
6. 3 키 주입 ('kid/jku/x5u')
시나리오:- 'jku' 는 공격자가 통제하는 JWKS를 가리 킵니다 (열쇠를 미끄러 뜨릴 것입니다).
- 'x5u '/' x5c' 외부 인증서 남용.
- 'kid' with/SQL 경로 주입 ('.. "/../privkey. pem "'또는'" OR 1 = 1- "').
- 엄격한 허용 목록 도메인으로 삭제 된 'jku/x5u' 를 무시하거나 필터링하십시오.
- 'kid' 는 파일 경로/SQL 연결없이 로컬 디렉토리 (테이블/캐시) 의 키로만 사용해야합니다.
- JWKS는 신뢰할 수있는 탭, 짧은 TTL, 서명/피닝 채널에서로드합니다.
6. HS256의 약한 비밀 4 개 (무차별)
결론: HMAC 비밀은 짧음/누출 → 서명 스푸핑입니다.
보호: 비대칭 (RS/ES/PS) 또는 비밀 길이 256 비트 이상, 비밀-KMS에서만 사용하십시오.
6. 5 누락/유효하지 않은 스탬프
No 'aud '/' iss '/' exp' → 토큰은 교차 서비스 가능하거나 무한합니다.
너무 긴 'exp' → 타협 위험.
보호: 'clock _ skew' 로 'exp' 쇼트, 'nbf '/' iat' 확인 전체 마크 세트가 필요합니다.
6. 6 재생 및 토큰 도난
본질: 토큰의 차단/반복 (TLS없이 로그, XSS, MitM에서 누출).
보호:- TLSite, 'Secure' + 'HttpOnly' 쿠키, SameSite = Lax/Strict.
- 파트너를위한 DPoP/PoP (클라이언트 키에 토큰 바인딩) 및/또는 mTLS.
- 짧은 'exp', 새로 고침 회전, 장치 바인딩.
6. XSS/스토리지 누출 7 개
결론: 'localStorage '/' sessionStorage' 의 JWT 스토리지는 → JS에서 사용할 수 있습니다.
보호: HttpOnly 쿠키 (쿠키 모델이 가능한 경우) + 엄격한 CSP/신뢰할 수있는 유형으로 액세스 토큰을 저장합니다.
쿠키가없는 SPA의 경우 메모리의 토큰을 최소한으로 분리하고 XSS로부터 보호하십시오.
6. 8 CSRF
결론: 쿠키 세션 중에 타사 사이트에서 요청합니다.
보호: SameSite, Anti-CSRF 토큰 (이중 제출), 'Origin/Referer' 확인, Fetch-Medata 필터.
6. 9 대체/크기 남용
요점: 거대한 페이로드/헤드 라인, DoS 구문 분석.
보호: 제목/신체 크기 제한, 조기 거부 431/413, 고정 스탬프 세트.
6. 10 대체 'typ '/' cty'
본질: 유형의 혼동 ('typ: "JWT"'), 중첩 된 JOSE 객체.
보호: 보안을 위해 'typ/cty' 를 무시하고 고정 알고리즘 및 브랜딩 체계에 의존합니다.
7) 토큰 보관 및 전송
7. 서버 API 1 개 (머신 투 머신)
mSL/HMAC, 바람직하게는 JWT의 비대칭 성은 메쉬를 통해 전달됩니다.
키 스토어-KMS/HSM, 예정된 회전, JWKS와 겹칩니다.
7. 브라우저 클라이언트 2
HttpOnly Secure Cookie 계정 액세스/새로 고침; 짧은 TTL; HTTPS에서만 'SameSite = None' 으로 "자동 새로 고침" 을 통해 업데이트하십시오.
엄격한 CSP, 신뢰할 수있는 유형은 XSS로부터 보호합니다. SPA의 경우-가능한 경우 디스크에 토큰을 저장하지 마십시오.
8) JWKS, 회전 및 리콜
JWKS는 승인자에 의해 출판됩니다. 소비자는 5-15 분 동안 캐시합니다.
회전 계획: 새로운 'kid' 를 추가하십시오 → N 일이 JWKS → 퍼지에서 오래된 것을 제거한 후 → 서명을 시작하십시오.
피드백: TTL이있는 'jti/sid' 목록; 사고가 발생하면 일시적으로 'exp' 및 강제 로그아웃 (새로 고침 비활성화) 을 줄입니다.
9) 멀티 테넌트 및 데이터 최소화
아키텍처에서 필요한 경우에만 토큰에 '테넌트 '/' org' 를 포함 시키십시오. 그렇지 않으면 PDP에서 'sub' 로 속성을 가져옵니다.
PII 없음; 최소 스탬프 세트 → 누출 및 상관 위험이 적습니다.
10) 실제 검증 점검표 (리소스 서버)
- 서명 및 기본 크기 제한을 검증 한 후에 만 Parsim.
- 구성에서 'alg'; 예기치 않은 것을 거부하
- 'iss를 확인하십시오' aud 'aud' exp 'exp' nbf '' iat '(' clock _ skew ').
- 로컬 디렉토리/JWKS (짧은 TTL) 에서 'kid' 를 확인하십시오.
- 외부 포인터를 필터/정규화 ('jku/x5u' -허용 목록 만).
- 스탬프 길이/구성 (체계) 을 제한합니다.
- 객체 자원 승인 적용 (BOLA 우선).
- 로그 'kid', 'sub', 'aud', 'iss', 'jti', 'exp', 'tenit', 'trace _ id' (PII 없음).
- 서명 오류, 지연, 회전 감사의 측정.
11) 보안 정책의 예 (의사)
11. 1 알고리즘 기대 설정
yaml jwt:
expected_issuer: "https://auth. example. com"
expected_audience: ["wallet-service"]
allowed_algs: ["ES256"] # fix the jwks_url: "https ://auth. example. com/.well-known/jwks. json"
jwks_cache_ttl: 600s clock_skew: 60s required_claims: ["iss","aud","sub","exp","iat"]
11. 2 원격 'jku/x5u' 삭제
yaml reject_untrusted_key_sources: true allowed_jku_hosts: ["auth. example. com"] # if absolutely necessary
11. 3 샘플 피드백 목록 (Redis)
pseudo if redis. exists("revoke:jti:" + jti) then deny()
if now() > exp then deny()
12) 관찰 및 발생률
'jwt _ valley _ fail _ total {reason}', 'jwt _ represed _ total', 'jwks _ refresh _ total', ло독점 'kid'.
로그 (구조화): 'iss/aud/sub/kid/jti/exp/tenter/trace _ id', 실패 이유.
대시 보드: "곧 만료", 검증 오류의 급증, 지역/클라이언트 별 배포.
경고: '확인 _ fail' (서명/알고리즘), JWKS 오류, 만료 된 토큰 공유.
13) 안티 패턴
토큰에서 'alg' 을 신뢰하십시오. '없음' 지원.
오래 지속되는 액세스 토큰과 새로 고침 회전이 없습니
KMS가없는 ENV에서 짧은 비밀/비밀이있는 HS256.
모든 도메인에서 'jku/x5u' 를 수락하십시오. 허용 목록없이 JWKS를 동적으로로드합니다.
PII/비밀을 페이로드 JWS에 넣습니다.
XSS 위험이 존재할 때 'localStorage' 에 토큰을 저장합니다.
검증 오류를 억제하십시오 (200 초 "소프트 오류" 반환).
BOLA는 확인하지 않으며 '범위/역할' 에만 의존합니다.
14) iGaming/Finance의 세부 사항
브랜드: 'kyc _ level', 'risk _ tier', 'tentian', 엄격한 'aud'.
쓰기 작업을위한 짧은 TTL (예금/출력), PoP/DPoP 또는 중요한 경로를위한 mTLS.
규제 감사: 변경 불가능한 입력/고장 로그, 해당 지역 경계 내의 로그 저장.
파트너/PSP에는 별도의 키/' aud ', 테넌트 키 및 별도의 JWKS가 있습니다.
15) Prod 준비 점검표
- 알고리즘의 어려운 허용 목록; '없음' 은 허용되지 않습니다.
- 'iss/aud/exp/nbf/iat/jti' 가 확인됩니다. 짧은 'exp'.
- 짧은 TTL 캐시 인 JWKS와 겹치는; '어린이' 주식 모니터링.
- 새로 고침-사용 중 회전; TTL이있는 'jti/sid' lists; 플레이 북 검토.
- 중요한 경로에서 PoP/DPoP 또는 mTLS.
- 브라우저 용 HttpOnly 쿠키, CSP/신뢰할 수있는 유형 대 XSS; CSRF 보호.
- 페이로드에 PII 없음; 최소 마크 세트.
- 검증 및 실패 지표/로그; (PHP 3 = 3.0.6, PHP 4)
- 부정적인 시나리오 테스트: RS → HS 교환, '키드' 주사, 'jku' 스푸핑, 대형, 시계 왜곡.
16) TL; DR
서버 측의 알고리즘과 키를 수정하고 토큰에서 'alg' 를 신뢰하지 말고 'iss/aud/exp/nbf/iat/jti' 를 요구하십시오. JWKS와 겹치는 키를 회전시키고 토큰을 짧게 유지하고 일회용으로 새로 고침하십시오. 토큰을 안전하게 저장하고 (웹 용 HttpOnly 쿠키) 스탬프를 최소화하고 PII를 넣지 마십시오. 'jku/x5u/kid' 벡터를 닫고 비밀이 약한 HS256을 피하고 중요한 경로에 PoP/DPoP 또는 mTLS를 추가하고 항상 리소스 레벨에서 BOLA 검사를 수행하십시오.