9분 읽기

JWT 서명 키는 어떻게 고를까?

JWT를 처음 붙이면 HS256이나 RS256이라는 이름을 마주친다. 둘 다 토큰의 signature를 만드는 알고리즘인데, 하나는 대칭키 방식이고 하나는 비대칭키 방식이라고 한다. 이름만 보면 “키가 하나냐 둘이냐” 정도로 끝날 것 같지만, 실제 선택은 토큰을 검증하는 서버에 발급 권한까지 줘도 되는가에 달려 있다.

이 글에서는 JWT 서명에서 자주 쓰는 두 방식을 비교한다.

  • HS256: HMAC with SHA-256. 공유한 비밀키로 MAC을 만드는 방식이다.
  • RS256: RSASSA-PKCS1-v1_5 with SHA-256. RSA 개인키로 서명하고 공개키로 검증하는 방식이다.

흔히 HMAC256, RSA256이라고 부르기도 하지만 JWT의 alg 값으로는 각각 HS256, RS256이 정확한 표기다. 두 이름의 정의는 JSON Web Algorithms(RFC 7518)에 정리되어 있다.

HS256과 RS256은 암호화가 아니다

JWT는 보통 아래처럼 점으로 나뉜 문자열이다.

base64url(header).base64url(payload).base64url(signature)

여기서 headerpayload는 base64url로 인코딩되어 있을 뿐이다. 키가 없어도 내용을 읽을 수 있으므로, 이메일 주소나 권한처럼 노출되면 안 되는 값을 payload에 넣어서는 안 된다.

signature의 역할은 두 가지다.

  1. 위·변조 방지: header나 payload가 한 글자라도 바뀌면 검증에 실패한다.
  2. 발급자 확인: 정해진 키를 가진 서버가 만든 토큰인지 확인한다.

즉 HS256과 RS256은 내용을 숨기는 기능이 아니라, 내용이 바뀌지 않았고 신뢰할 수 있는 발급자가 만들었다는 것을 검증하는 수단이다. JWS는 디지털 서명 또는 MAC으로 내용을 보호하고, 암호화는 별도 규격인 JWE가 담당한다는 점도 JWS(RFC 7515)에서 구분한다.

비밀번호와 도장으로 비교하기

HS256은 인증 서버와 API 서버가 같은 공유 비밀키를 가지고, RS256은 인증 서버만 개인키를 가지며 API 서버는 공개키로 검증하는 구조
둘 다 토큰 내용을 암호화하지 않는다. 차이는 서명에 쓰는 키와 검증에 쓰는 키를 누가 보관하는가에 있다.

HS256은 같은 비밀키를 나눠 쓴다

HS256은 인증 서버와 API 서버가 같은 비밀키를 가진다. 인증 서버는 그 비밀키로 메시지 인증 코드(MAC)를 만들고, API 서버는 같은 계산을 다시 해 결과를 비교한다.

인증 서버:  HMAC-SHA-256(공유 비밀키, header.payload) → signature
API 서버:   HMAC-SHA-256(공유 비밀키, header.payload) == signature ?

사무실의 공동 출입 비밀번호를 떠올리면 된다. 비밀번호를 아는 사람은 문을 열 수 있고, 같은 비밀번호를 알고 있다면 새 비밀번호 확인표를 만들 수도 있다.

이것이 HS256의 가장 중요한 특징이다. 검증할 수 있는 서버는 새 토큰도 만들 수 있다. API 서버가 공유 비밀키를 가지고 있기 때문이다. HMAC은 비밀키와 해시 함수를 조합해 MAC을 만들며, HS256에는 최소 256비트의 키가 요구된다. RFC 7518의 HS256 규정HMAC(RFC 2104)을 참고할 수 있다.

RS256은 개인키와 공개키를 나눠 쓴다

RS256에서는 인증 서버가 개인키를 혼자 보관한다. 이 개인키로만 토큰에 서명할 수 있다. API 서버에는 짝이 되는 공개키만 배포한다. 공개키는 서명이 맞는지 확인할 수 있지만, 같은 모양의 새 서명을 만들 수는 없다.

인증 서버:  RSA 개인키로 sign(header.payload) → signature
API 서버:   RSA 공개키로 verify(header.payload, signature)

이는 인감 도장과 도장 확인표에 가깝다. 도장은 인증 서버 금고에만 있고, API 서버는 찍힌 도장이 진짜인지 확인하는 방법만 안다. API 서버가 침해되더라도 공개키만으로는 새 토큰을 발급할 수 없다.

JWA에서 RS256RSASSA-PKCS1-v1_5와 SHA-256의 조합을 뜻하며, 2,048비트 이상의 RSA 키를 요구한다. 정확한 서명 절차와 키 길이는 RFC 7518의 RS256 규정에 있다.

인증 서버 하나와 여러 API 서버가 있다면

사이드 프로젝트가 커져 인증 서버와 주문·결제·알림 API가 분리됐다고 해 보자. 로그인은 인증 서버만 처리하지만, 각 API 서버는 요청마다 access token을 검증해야 한다.

HS256을 쓰면

모든 API 서버에 JWT_SECRET을 배포해야 한다.

인증 서버 ─┐
주문 API  ─┼─ 같은 JWT_SECRET 보관
결제 API  ─┤
알림 API  ─┘

구성은 단순하다. 비밀키 하나를 각 서버의 Secret으로 주입하면 된다. 인증 서버와 검증 서버를 같은 서비스 내부에서 함께 관리하고, 검증하는 서비스 수가 적다면 이 방식은 충분히 실용적이다.

다만 주문 API 한 대에서 비밀키가 유출되면 공격자는 주문 API뿐 아니라 인증 서버인 것처럼 토큰을 발급할 수 있다. 키를 교체하려면 모든 검증 서버의 비밀키를 함께 교체해야 한다는 운영 부담도 생긴다.

RS256을 쓰면

개인키는 인증 서버 또는 별도의 키 관리 시스템에만 둔다. API 서버에는 공개키만 배포한다. 공개키는 JWKS 엔드포인트로 제공하고, API 서버가 주기적으로 가져가게 구성하기도 한다.

인증 서버: 개인키 보관, 토큰 서명
API 서버들: 공개키 보관 또는 JWKS 조회, 토큰 검증

검증 서버가 늘어나거나, 서로 다른 팀이 운영하는 서비스가 토큰을 검증해야 한다면 이 경계가 특히 유용하다. 공개키가 유출돼도 서명 권한은 생기지 않기 때문이다. 키를 교체할 때도 새 공개키를 먼저 배포하고, 기존 토큰의 만료 시간을 고려해 이전 공개키를 잠시 함께 유지하는 식으로 전환할 수 있다.

여기서 “RS256이 항상 더 안전하다”라고 결론 내리면 안 된다. 개인키를 안전하게 보관하지 못하거나, 검증 규칙을 느슨하게 두면 RS256도 안전하지 않다. 차이는 알고리즘 이름이 아니라 서명 권한을 어디까지 배포하느냐에 있다.

어떤 방식을 선택할까?

상황더 자연스러운 선택이유
인증 서버와 검증 서버가 하나이거나, 모두 같은 서비스 안에서 관리된다HS256공유 비밀키 관리가 단순하다.
여러 API 서버가 토큰을 검증하지만, 발급은 인증 서버만 해야 한다RS256검증 서버에 개인키를 주지 않아도 된다.
외부 파트너나 다른 조직의 서비스도 검증해야 한다RS256공개키만 공유해 검증 권한을 나눌 수 있다.
토큰을 검증하는 서비스가 계속 늘어난다RS256비밀키를 배포하는 범위를 인증 서버로 제한할 수 있다.

성능만 보고 고르면 판단이 흐려질 수 있다. 일반적으로 HMAC 계산은 RSA 서명보다 가볍지만, 실제 병목은 토큰 검증 빈도, 키 조회와 캐시, 네트워크, 사용하는 라이브러리에 따라 달라진다. 먼저 “누가 토큰을 발급할 수 있어야 하는가”를 정하고, 그다음 부하를 측정하는 편이 안전하다.

두 방식에서 모두 지켜야 할 것

HS256과 RS256 중 무엇을 쓰든 아래는 공통이다.

1. 허용할 알고리즘을 서버에서 정하기

검증 서버는 자신이 허용한 알고리즘을 설정으로 고정해야 한다. 예를 들어 RS256만 쓰기로 했다면 alg=RS256만 허용하고, HS256용 비밀키로 검증을 시도하지 않아야 한다. JWT Best Current Practices는 수신한 JWT의 alg에 따라 허용 알고리즘을 명시적으로 검증하라고 권고한다. 자세한 내용은 RFC 8725, 3.1절을 참고하면 된다.

2. 비밀키와 개인키를 안전하게 보관하기

HS256의 공유 비밀키와 RS256의 개인키를 Git 저장소, 로그, 에러 메시지에 남기면 안 된다. 환경 변수로 넣는다고 자동으로 안전해지는 것도 아니다. Kubernetes Secret, 클라우드 Secret Manager, 전용 키 관리 시스템처럼 접근을 제한하고 변경 이력을 남길 수 있는 곳에서 관리해야 한다.

특히 HS256은 검증 서버에도 발급 권한을 주는 구조다. “이 서버가 토큰을 검증한다”는 이유만으로 비밀키를 넓게 배포하지 않는 것이 좋다.

3. 서명 뒤에 토큰 내용도 확인하기

서명이 유효하다는 것은 토큰이 위·변조되지 않았다는 뜻이다. 그것만으로 지금 이 요청을 허용해도 된다는 뜻은 아니다. 토큰 안의 정보인 claim에서 exp(만료), iss(발급자), aud(대상), 필요한 권한을 애플리케이션이 따로 확인해야 한다.

핵심 정리

대칭키와 비대칭키의 차이는 단순히 키가 하나냐 둘이냐가 아니다. JWT 서명에서는 검증 권한과 발급 권한을 분리할 수 있는가의 차이다.

  • HS256은 같은 공유 비밀키로 서명하고 검증한다. 간단하지만, 검증자도 서명자가 될 수 있다.
  • RS256은 개인키로 서명하고 공개키로 검증한다. 검증 서버를 늘려도 개인키를 배포하지 않아도 된다.
  • 둘 다 payload를 암호화하지 않는다. 민감한 정보는 토큰에 넣지 말고, 필요한 경우 JWE나 별도의 데이터 보호 방식을 검토해야 한다.

인증 서버 하나와 소수의 내부 서비스라면 HS256이 합리적일 수 있다. 반대로 인증 서버만 토큰을 만들게 하고 여러 서비스가 검증해야 한다면 RS256의 공개키 구조가 더 잘 맞는다. 결국 선택 기준은 “검증하는 서버에도 토큰 발급 권한을 줄 것인가”다.

참고 자료