HS256과 RS256을 비교할 때 저는 “어느 알고리즘이 더 강한가?”보다 먼저 누가 signing key를 가지고 있고, 누가 verification만 해야 하는가를 봅니다. 두 방식 모두 적절하게 구현하면 JWT 서명에 사용할 수 있지만 운영 모델은 크게 다릅니다.
제가 선택 기준을 한 문장으로 줄이면 이렇습니다.
한 시스템 안에서 작은 trust boundary를 유지할 수 있으면 HS256이 단순할 수 있고, 여러 verifier가 issuer의 signing 권한 없이 검증해야 하면 RS256이 더 자연스럽습니다.
HS256의 trust 모델
HS256은 HMAC SHA-256을 사용합니다. issuer와 verifier가 같은 secret을 공유합니다.
Issuer ─┐
├─ shared secret → sign / verify
Verifier┘
이 구조의 장점은 단순함입니다. key pair나 JWKS가 필요하지 않고, 작은 내부 시스템에서는 구현과 운영이 비교적 쉽습니다.
하지만 중요한 성질이 하나 있습니다.
HS256 secret을 가진 verifier는 signature를 검증할 수 있을 뿐 아니라 새로운 valid signature도 만들 수 있습니다.
즉 verification 권한과 signing 권한이 분리되지 않습니다.
RS256의 trust 모델
RS256은 RSA private key로 서명하고 public key로 검증합니다.
Issuer
private key → sign
↓
JWT
↓
Verifiers
public key → verify
API, gateway, background worker가 public key만 가지면 issuer의 private signing key를 공유하지 않아도 됩니다.
저는 다음 상황에서 이 분리가 특히 중요하다고 봅니다.
- 하나의 issuer가 여러 서비스에 token을 발급하는 경우
- verifier가 여러 팀/환경에 분산된 경우
- third-party 또는 별도 trust domain이 검증하는 경우
- 공개 JWKS와 key rotation을 운영하려는 경우
제가 비교할 때 보는 핵심 차이
| 기준 | HS256 | RS256 |
|---|---|---|
| key 형태 | shared secret | private/public key pair |
| signing 권한 | secret 보유자 모두 가능 | private key 보유자만 가능 |
| verification | 같은 secret 필요 | public key로 가능 |
| 여러 verifier 배포 | secret 확산 위험 | public key 배포 용이 |
| JWKS 친화성 | 일반적으로 부적합 | 매우 적합 |
| key rotation | secret 교체 조정 필요 | kid + JWKS로 운영하기 쉬움 |
| 구현 복잡도 | 낮음 | 더 높음 |
이 표만 보고 무조건 RS256을 선택하지는 않습니다. 대신 시스템 경계와 key 관리 책임을 먼저 정합니다.
HS256을 선택하는 경우
제가 HS256을 고려하는 조건은 비교적 명확합니다.
- token issuer와 verifier가 같은 보안 경계 안에 있습니다.
- verifier 수가 적습니다.
- secret distribution을 안전하게 관리할 수 있습니다.
- public verification 요구가 없습니다.
- 운영 복잡도를 최소화할 가치가 큽니다.
예를 들어 하나의 backend가 token을 만들고 같은 backend가 token을 검증한다면 HS256은 충분히 합리적일 수 있습니다.
하지만 microservice가 늘어나면서 모든 서비스에 같은 secret을 복제하기 시작하면 저는 다시 검토합니다.
RS256을 선택하는 경우
다음과 같은 경우 저는 RS256 또는 다른 적절한 비대칭 JWS 알고리즘을 우선 검토합니다.
- issuer는 한 곳이고 verifier는 많습니다.
- verifier가 signing capability를 가져서는 안 됩니다.
kid와 JWKS를 사용한 key selection이 필요합니다.- rotation 중 old/new public key를 동시에 배포해야 합니다.
- identity provider가 공개 metadata와 JWKS를 제공합니다.
이 모델에서는 issuer가 private key를 제한된 환경에 보관하고 verifier는 public key만 받습니다.
alg는 token이 결정하게 두지 않습니다
HS256과 RS256을 비교할 때 가장 위험한 구현 중 하나는 token header의 alg를 보고 런타임에 검증 방법을 자유롭게 바꾸는 방식입니다.
저는 verifier에 명시적인 allowlist를 둡니다.
allowed algorithms = ["RS256"]
그리고 token이 다른 alg를 주장하면 거부합니다.
RFC 8725도 verifier가 허용 algorithm을 명시적으로 제한해야 한다는 방향을 강조합니다. JWT Security Analyzer에서도 allowed algorithm을 직접 넣어 예상치 못한 alg를 확인할 수 있습니다.
Key 길이와 품질도 알고리즘 이름만큼 중요합니다
HS256을 사용하면서 너무 짧거나 사람이 기억하기 쉬운 secret을 쓰면 안전하지 않습니다. 저는 충분한 entropy를 가진 random secret을 사용하고 source code나 client bundle에 넣지 않습니다.
RS256에서는 private key 보호가 핵심입니다. public key는 배포해도 되지만 private key는 issuer의 signing 환경 밖으로 확산시키지 않습니다.
즉 “RS256이니까 안전하다”가 아니라:
알고리즘 + key quality + key storage + verifier policy
전체가 함께 맞아야 합니다.
Rotation 관점에서의 차이
HS256 rotation
shared secret을 바꾸면 issuer와 모든 verifier가 새 secret 상태에 맞아야 합니다. transition 동안 두 secret을 지원하는 구현이 필요할 수 있지만 잘못하면 secret 수명이 길어지고 관리가 복잡해집니다.
RS256 rotation
RS256은 보통 kid와 JWKS를 활용합니다.
old private key → old kid
new private key → new kid
JWKS:
old public JWK
new public JWK
새 key를 미리 publish하고, 새 token에 new kid를 사용하기 시작한 뒤, old token의 최대 수명이 지나면 old key를 제거할 수 있습니다.
이 흐름은 JWT Key Rotation 가이드에서 단계별로 다룹니다.
JWKS와 함께 쓰는 RS256
제가 RS256 token을 디버깅할 때 보통 다음 순서입니다.
- token을 decode합니다.
- header에서
alg와kid를 확인합니다. - expected issuer를 확인합니다.
- issuer의 신뢰할 수 있는 JWKS를 가져옵니다.
kid,kty,alg,use,key_ops로 후보 key를 좁힙니다.- signature를 실제로 검증합니다.
iss,aud,exp,nbf를 검증합니다.
이 과정을 한 번에 확인하려면 JWKS Validator를 사용합니다.
HS256 secret을 브라우저에 넣지 않는 이유
브라우저는 public client 환경입니다. 사용자에게 전달되는 JavaScript bundle이나 runtime state에 signing secret을 넣는 순간 그 secret은 더 이상 secret이라고 보기 어렵습니다.
따라서 저는 client-side app이 HS256 token을 독립적으로 검증하도록 production secret을 배포하지 않습니다. browser에서 필요한 것은 보통 token 내용 inspection이고, trust 판단은 backend 또는 비대칭 public key 모델에서 처리합니다.
제가 흔히 보는 잘못된 선택 기준
“HS256이 빠르니까 무조건 HS256”
JWT verification에서 일반 애플리케이션 병목은 보통 알고리즘 몇 마이크로초 차이보다 network, database, business logic입니다. trust architecture가 먼저입니다.
“RS256이 더 길고 복잡하니까 무조건 더 안전”
private key가 유출되거나 issuer/audience 검증이 빠지면 RS256이어도 시스템은 안전하지 않습니다.
“token의 alg가 RS256이라고 써 있으니 RS256으로 검증”
header는 검증 전 데이터입니다. 서버 정책이 허용 알고리즘을 먼저 결정해야 합니다.
제 선택 체크리스트
HS256과 RS256 중 결정할 때 저는 다음 질문을 합니다.
- signer와 verifier가 같은 trust boundary인가?
- verifier가 signing 권한을 가져도 되는가?
- verifier 수는 얼마나 되는가?
- key를 여러 서비스에 어떻게 배포할 것인가?
- rotation 시 old/new key를 어떻게 겹쳐 운영할 것인가?
- identity provider가 JWKS를 제공하는가?
- algorithm allowlist는 어디에서 강제되는가?
- issuer와 audience 검증은 어디에서 하는가?
작은 내부 시스템에서는 HS256의 단순함이 장점일 수 있습니다. 반대로 여러 verifier가 독립적으로 token을 검증하는 환경에서는 RS256의 signing 권한과 verification 권한 분리가 더 중요한 장점이 됩니다.
결국 제가 선택하는 기준은 알고리즘 이름의 인기도가 아니라 key ownership과 trust boundary를 얼마나 명확하게 만들 수 있는가입니다.