JWT header의 kid를 볼 때 저는 이것을 key를 찾기 위한 identifier로 사용하지만, 절대 “이 key는 신뢰할 수 있다”는 증명으로 취급하지 않습니다.
예를 들어 header가 다음과 같다고 가정합니다.
{
"alg": "RS256",
"kid": "2026-09-key-b"
}
이 kid가 하는 일은 trusted JWKS 안에서 후보 key를 찾는 데 도움을 주는 것입니다.
제가 사용하는 trust 흐름
configured issuer
→ trusted JWKS
→ kid로 candidate JWK 선택
→ alg/kty/use/key_ops 확인
→ signature verification
→ iss/aud/time validation
중요한 순서는 trusted JWKS가 먼저이고 kid가 그 다음이라는 점입니다.
kid는 왜 필요한가
issuer는 동시에 여러 signing key를 공개할 수 있습니다. 특히 key rotation 중에는 old/new key가 함께 JWKS에 있을 수 있습니다.
{
"keys": [
{ "kid": "old-key", "kty": "RSA", "...": "..." },
{ "kid": "new-key", "kty": "RSA", "...": "..." }
]
}
JWT header의 kid는 verifier가 어떤 key를 먼저 검토할지 알려줍니다.
kid가 맞으면 signature도 맞는가
아닙니다.
같은 kid 문자열을 가진 JWK를 찾았다는 것은 key selection 단계가 진행됐다는 뜻일 뿐입니다. 실제 signature를 cryptographically verify해야 합니다.
JWKS Validator에서 저는 key matched와 signature verified를 별도 상태로 봅니다.
제가 candidate key에서 추가로 확인하는 값
kty
RS256이라면 RSA key가 자연스럽습니다. EC key를 RS256 candidate로 쓰지 않습니다.
alg
JWK에 alg가 명시되어 있다면 token과 verifier allowlist에 맞는지 확인합니다.
use
use가 있다면 signature verification에는 보통 sig를 기대합니다.
key_ops
key_ops가 있다면 verify operation을 허용하는지 봅니다.
curve
ES256/ES384/ES512 같은 EC algorithm에서는 key curve compatibility도 확인합니다.
즉 kid 하나만으로 key를 확정하지 않습니다.
Unknown kid가 나오면 어떻게 하는가
rotation 직후 자주 보이는 오류입니다.
JWT kid = new-key
cached JWKS = old-key only
이 경우 저는 verifier가 다음처럼 동작하도록 설계합니다.
- cached JWKS 검색
- 없으면 rate-limit된 refresh
- refreshed JWKS에서 다시 검색
- 그래도 없으면 거부
unknown kid마다 무조건 remote fetch하면 공격자가 random kid를 보내 refresh storm을 만들 수 있습니다.
JWT Key Rotation 가이드에서 cache와 overlap을 자세히 설명합니다.
Duplicate kid가 있으면 어떻게 하는가
JWKS 안에 같은 kid가 여러 개 있고 다른 조건으로도 하나를 명확하게 선택할 수 없다면 저는 ambiguity를 오류로 처리합니다.
첫 번째 배열 원소를 무조건 고르는 방식은 key set ordering에 verification 결과가 의존하게 만듭니다.
가능하면 issuer가 unique kid를 운영하도록 하는 것이 좋습니다.
kid가 없는 JWT는 invalid인가
항상 그런 것은 아닙니다. JWKS에 compatible key가 하나뿐이거나 다른 방식으로 key를 명확히 선택할 수 있는 시스템도 있습니다.
하지만 여러 key와 rotation이 있는 환경에서는 kid가 매우 유용합니다.
저는 “오늘 key가 하나”라는 이유만으로 future rotation을 무시하지 않습니다.
kid를 파일 경로나 SQL query로 직접 쓰지 않습니다
kid는 untrusted token header에서 온 문자열입니다. 과거 여러 구현에서 kid를 file name, database query, command/path에 안전하지 않게 연결해 문제가 생길 수 있었습니다.
저는 kid를:
- 길이 제한
- 허용 문자 정책
- exact in-memory/JWKS lookup
처럼 단순한 식별자로 다룹니다.
../, slash, control character 같은 의심스러운 값을 경로에 연결하지 않습니다.
JWT Security Analyzer도 긴 kid, control character, path-like 패턴을 별도 signal로 표시합니다.
kid와 jku를 섞지 않습니다
kid는 trusted key set 안에서 key를 고르는 데 사용할 수 있습니다. 반면 jku는 remote JWK Set URL을 나타낼 수 있습니다.
제가 피하는 흐름:
JWT의 jku를 자동 fetch
→ 그 JWKS에서 kid 찾음
→ signature verify
이 방식은 token이 자기 key source를 선택하게 만들 수 있습니다.
제가 원하는 방식:
configured issuer
→ trusted metadata/JWKS
→ kid lookup
입니다.
Key rotation에서 kid를 재사용하지 않는 이유
새 key material에 old kid를 다시 사용하면 cache가 old key를 들고 있는 동안 verifier가 잘못된 key를 사용할 수 있습니다.
그래서 저는 보통 new key pair에 new kid를 부여합니다.
JWK Inspector에서 RFC 7638 thumbprint를 비교하면 실제 public key material이 같은지 확인하는 데 도움이 됩니다.
Invalid signature와 kid 관계를 추적합니다
invalid signature가 발생하면 저는 다음을 순서대로 봅니다.
- token의
kid - expected issuer의 JWKS
kidmatching candidate 수- candidate JWK의 metadata compatibility
- actual public key material
- signature verification
- JWKS cache와 rotation 상태
자세한 흐름은 JWT Invalid Signature 가이드를 사용합니다.
제가 로그에 남기는 kid 정보
production에서 full token을 남기기보다 다음 정도는 diagnostic metadata로 유용할 수 있습니다.
kidalg- issuer
- verifier result category
- JWKS refresh 여부
단, kid 역시 외부 입력이므로 로그 injection을 막도록 구조화된 logging을 사용합니다.
테스트 케이스
저는 최소 다음 key-selection fixture를 둡니다.
matching unique kid → candidate selected
unknown kid → refresh 후 명확히 fail/pass
duplicate kid → ambiguous fail
kid matches but wrong kty → fail
kid matches but wrong alg → fail
kid matches but signature wrong → fail
이 테스트는 “kid가 있으면 통과” 같은 잘못된 최적화를 막습니다.
제 kid 체크리스트
kid를 untrusted identifier로 취급합니다.- 먼저 trusted issuer/JWKS를 결정합니다.
- 그 set 안에서 exact kid match를 찾습니다.
- duplicate/ambiguity를 거부합니다.
kty,alg,use,key_ops, curve compatibility를 확인합니다.- 실제 signature를 검증합니다.
- unknown kid refresh를 rate-limit합니다.
- rotation에서는 new key에 new kid를 사용합니다.
- kid를 path/query/command에 직접 연결하지 않습니다.
- signature 성공 뒤 issuer/audience/time 정책도 검증합니다.
제가 kid를 보는 핵심 관점은 “어떤 key를 시도할지 알려주는 힌트”와 “그 key를 신뢰할 근거”를 분리하는 것입니다. kid는 선택을 돕지만 trust는 configured issuer, trusted key source와 실제 signature verification에서 만들어집니다.