JWT를 검증할 때 저는 iss를 단순한 문자열 claim이 아니라 어떤 key source와 정책을 신뢰할지 결정하는 출발점으로 봅니다. signature가 cryptographically 맞더라도 내가 신뢰하지 않는 issuer가 발급한 token이라면 API가 받아서는 안 될 수 있습니다.
그래서 저는 issuer → metadata/JWKS → key selection → signature → claims 순서를 유지합니다.
iss claim이 하는 일
예를 들어 payload가 다음과 같다고 가정합니다.
{
"iss": "https://id.example.com/",
"aud": "https://api.example.com",
"exp": 1789323600
}
iss는 이 token을 발급했다고 주장하는 주체입니다. 하지만 token 안에 적혀 있다는 이유로 그 issuer를 자동으로 신뢰하지는 않습니다.
애플리케이션 설정에서 expected issuer를 미리 알고 있어야 합니다.
expected issuer = https://id.example.com/
Exact match를 사용하는 이유
issuer는 security boundary이므로 저는 가능한 한 exact comparison을 사용합니다.
다음은 비슷해 보여도 다른 값입니다.
https://id.example.com
https://id.example.com/
또한:
https://id.example.com/tenant-a
https://id.example.com/tenant-b
를 prefix만 보고 같은 issuer처럼 취급하지 않습니다.
임의 normalization으로 mismatch를 숨기기보다 provider metadata와 애플리케이션 설정을 정확히 맞춥니다.
Token이 알려주는 issuer를 그대로 따라가지 않습니다
나쁜 흐름은 다음과 같습니다.
JWT payload의 iss를 읽음
→ 그 URL을 자동으로 신뢰
→ metadata/JWKS fetch
→ 그 key로 signature verify
이렇게 하면 공격자가 만든 token이 자기 자신을 검증할 issuer/key source를 선택할 수 있습니다.
제가 원하는 흐름은:
application config가 expected issuer를 알고 있음
→ token iss가 exact match인지 확인
→ trusted issuer metadata/JWKS 사용
→ signature verify
입니다.
Metadata에서 무엇을 확인하는가
OIDC Discovery 또는 OAuth Authorization Server Metadata를 사용하는 환경에서는 issuer metadata에 다음 같은 값이 있습니다.
{
"issuer": "https://id.example.com/",
"jwks_uri": "https://id.example.com/.well-known/jwks.json"
}
저는 discovery를 시작할 때 사용한 issuer와 metadata가 반환한 issuer가 정확히 일치하는지 확인합니다.
OIDC Discovery Checker에서 이 exact issuer check와 jwks_uri 구조를 볼 수 있습니다.
Issuer와 JWKS를 함께 binding합니다
JWT verification에서 중요한 것은 public key가 “어떤 key든 맞는가”가 아니라 신뢰한 issuer에 연결된 key인가입니다.
제가 생각하는 trust chain은:
configured issuer
↓
trusted metadata
↓
trusted jwks_uri
↓
JWKS keys[]
↓
kid / algorithm compatibility
↓
signature verification
입니다.
다른 issuer의 public key로 signature가 우연히 맞는 상황을 허용하지 않습니다.
Multi-tenant issuer를 특히 조심합니다
identity provider에 따라 tenant마다 issuer가 다르거나 공통 endpoint와 tenant-specific issuer가 섞일 수 있습니다.
저는 provider 문서를 보고:
- tenant-specific issuer를 요구하는지
- 특정 tenant allowlist가 필요한지
- multi-tenant token을 어떤 규칙으로 받아야 하는지
명시적으로 결정합니다.
iss 문자열 일부만 확인하거나 domain suffix만 보고 신뢰하지 않습니다.
Issuer mismatch와 audience mismatch를 분리합니다
두 오류는 함께 나타날 수 있지만 의미가 다릅니다.
issmismatch: 내가 신뢰하는 발급자인가?audmismatch: 이 token이 내 API를 대상으로 발급됐는가?
둘 다 통과해야 합니다.
JWT Audience Mismatch 가이드에서 audience 검증을 별도로 다룹니다.
Signature 검증 전에 issuer를 볼 수 있는가
payload를 decode하면 iss를 읽을 수는 있습니다. 하지만 아직 그 값 자체를 신뢰하지 않습니다.
실무적으로는 unverified iss를 사용해 configured issuer 후보를 선택할 수 있는 시스템도 있지만, 선택 가능한 issuer 목록은 애플리케이션이 미리 제한해야 합니다. 임의 issuer를 동적으로 신뢰하는 구조는 피합니다.
Key rotation에서도 issuer binding이 중요합니다
rotation 중 new kid가 나타났을 때 verifier가 해야 할 일은 인터넷 전체에서 key를 찾는 것이 아닙니다.
known issuer의 JWKS를 refresh
→ new kid가 trusted set에 나타나는지 확인
입니다.
JWT Key Rotation 가이드에서 unknown kid와 cache refresh를 다룹니다.
jku/x5u를 issuer 대체물로 사용하지 않습니다
JWT header의 jku나 x5u가 remote key URL을 제공할 수 있지만 저는 token이 지정한 URL을 자동으로 신뢰하지 않습니다.
key source는 configured issuer/metadata에 binding되어야 합니다. 그래야 SSRF와 attacker-controlled key selection 같은 위험을 줄일 수 있습니다.
제가 issuer 오류를 디버깅하는 순서
- JWT Decoder에서 raw
iss를 확인합니다. - API 설정의 expected issuer 값을 확인합니다.
- trailing slash, tenant path 등 문자열 차이를 그대로 비교합니다.
- OIDC/OAuth metadata가 반환하는 issuer를 확인합니다.
- metadata의
jwks_uri가 expected issuer trust chain에 속하는지 봅니다. - token
kid가 그 JWKS에서 선택되는지 확인합니다. - 실제 signature를 검증합니다.
- 그 다음 audience와 time claim을 검증합니다.
JWKS Validator에서 expected issuer를 함께 지정하면 key와 claim validation을 한 번에 재현할 수 있습니다.
제가 피하는 패턴
모든 issuer 허용
signature만 맞으면 issuer를 무시하는 방식은 multi-issuer 시스템에서 trust boundary를 잃기 쉽습니다.
Domain suffix만 비교
endsWith("example.com") 같은 느슨한 비교는 정확한 issuer identity를 보장하지 않습니다.
Token의 iss로 임의 metadata fetch
untrusted input이 network destination과 trust source를 동시에 결정하게 하지 않습니다.
Issuer mismatch를 문자열 trim으로 조용히 고침
설정과 provider metadata를 바로잡는 것이 우선입니다.
테스트 케이스
저는 최소 다음 테스트를 둡니다.
correct issuer → pass
wrong issuer → fail
missing issuer → policy에 따라 fail
trailing slash mismatch → fail unless configured exactly
correct signature from another issuer → fail
이 테스트는 “signature만 맞으면 된다”는 regression을 막는 데 중요합니다.
제 최종 체크리스트
- expected issuer를 애플리케이션 설정으로 명시합니다.
- token
iss와 exact match합니다. - issuer metadata의 returned issuer도 exact match합니다.
- JWKS source를 issuer에 binding합니다.
- token이 제공한 remote key URL을 무조건 신뢰하지 않습니다.
- trusted set 안에서
kid와 algorithm/key compatibility를 확인합니다. - signature를 검증합니다.
- audience, expiration, token type과 권한을 별도로 검증합니다.
제가 issuer validation을 중요하게 보는 이유는 iss 하나를 검사하기 위해서가 아닙니다. “이 token의 암호학적 신뢰를 어느 주체와 어느 key set에 연결할 것인가”를 명확하게 하기 위해서입니다.