JWT 서명 알고리즘 비교: HS256 vs RS256 vs ES256
JWT나 JWS로 토큰에 서명하려고 보면 alg 자리에 뭘 넣어야 할지부터 막히는데요.
HS256, RS256, ES256, EdDSA… 이름은 비슷비슷한데 막상 고르려니 뭐가 다른지 감이 안 옵니다. 🤔
이 알고리즘들은 모두 JWA(JSON Web Algorithms, RFC 7518)가 정의한 서명 알고리즘인데요. 그냥 아무거나 골라도 토큰은 만들어지지만, 대칭이냐 비대칭이냐, 토큰이 얼마나 커지느냐, 검증 부하가 어디에 실리느냐에 따라 분명한 트레이드오프가 있습니다. 이번 글에서는 자주 쓰이는 네 가지를 비교하고 상황별로 무엇을 고르면 좋을지 정리해 보겠습니다.
알고리즘 이름 읽는 법
암호처럼 보이는 이름이지만 규칙은 단순합니다. 앞부분은 서명 방식, 뒷부분 숫자는 해시 강도예요.
HS256— HMAC + SHA-256. 비밀키 하나로 서명/검증하는 대칭 방식RS256— RSA + SHA-256. 개인키로 서명, 공개키로 검증하는 비대칭 방식ES256— ECDSA(타원 곡선 P-256) + SHA-256. 역시 비대칭EdDSA— Ed25519 곡선 기반의 최신 비대칭 서명
가장 큰 갈림길은 대칭이냐 비대칭이냐입니다. 대칭은 서명과 검증에 같은 키를 쓰고, 비대칭은 비대칭키 암호화처럼 개인키와 공개키가 한 쌍을 이룹니다. 이 차이가 키를 어떻게 배포하고 누가 토큰을 만들 수 있는지를 결정합니다.
HS256: 가장 단순하고 빠른 대칭 방식
HS256은 비밀키 하나로 서명도 하고 검증도 합니다.
구현이 단순하고 가장 빠르며, 토큰에 붙는 서명도 짧습니다.
문제는 검증하는 쪽도 똑같은 비밀키를 가져야 한다는 점입니다. 토큰을 발급하는 서버와 검증하는 서버가 같은 한 곳이라면 더없이 편하지만, 검증하는 쪽이 여러 곳이라면 그 모두에게 비밀키를 나눠줘야 하죠. 키를 가진 누구나 위조 토큰을 만들 수 있으니, 키가 한 곳이라도 새면 전체가 뚫립니다.
그래서 HS256은 단일 서비스 내부에서 토큰을 발급하고 바로 검증하는 경우에 적합합니다.
RS256: 가장 널리 호환되는 비대칭 방식
RS256은 RSA 기반의 비대칭 서명입니다.
발급 서버는 개인키로 서명하고, 검증하는 쪽은 공개키로만 검증합니다.
공개키는 새어나가도 위조에 쓸 수 없으니 마음껏 배포할 수 있어요.
실제로 OAuth/OIDC 생태계에서 인가 서버가 공개키를 JWKS로 공개하고 여러 자원 서버가 받아 쓰는 구조가 바로 이 방식입니다.
가장 오래되고 호환성이 좋아 “고민되면 RS256”이라는 말이 있을 정도인데요. 대신 RSA는 서명 연산이 무겁고, 키와 서명값이 큽니다. 뒤에서 수치로 보겠지만 토큰 크기가 다른 방식의 두세 배에 달합니다.
ES256: 작고 효율적인 비대칭 방식
ES256은 타원 곡선(ECDSA)을 쓰는 비대칭 서명입니다.
RSA와 같은 비대칭의 장점(공개키 배포)을 누리면서도 키와 서명이 훨씬 작고 서명이 빠릅니다.
RSA-2048과 비슷한 보안 강도를 256비트짜리 작은 키로 달성하거든요.
토큰 크기와 성능이 중요한 환경이라면 RS256보다 ES256이 합리적인 선택입니다.
다만 오래된 일부 시스템은 ECDSA 지원이 빠질 수 있어, 연동 상대가 받아주는지 확인이 필요합니다.
EdDSA: 더 단순하고 안전한 최신 방식
EdDSA(Ed25519)는 가장 최근에 표준에 들어온 서명 알고리즘입니다.
ECDSA 계열의 작고 빠른 장점은 그대로 가지면서, 구현상의 함정(예: 난수 재사용으로 개인키가 노출되는 문제)을 설계로 막아 더 안전하다고 평가받습니다.
새로 시작하는 프로젝트이고 양쪽 다 지원한다면 EdDSA나 ES256이 가장 무난한 현대적 선택입니다.
한눈에 비교
실제로 같은 claim에 각 알고리즘으로 서명해 토큰 크기와 서명 길이를 재봤습니다(서명 길이는 Base64url 문자 수 기준).
| 알고리즘 | 종류 | 토큰 길이 | 서명 길이 | 특징 |
|---|---|---|---|---|
HS256 | 대칭 | 111 | 43 | 가장 작고 빠름, 키 공유 필요 |
RS256 | 비대칭 | 410 | 342 | 호환성 최고, 토큰이 큼 |
ES256 | 비대칭 | 154 | 86 | 작고 균형 잡힘 |
EdDSA | 비대칭 | 154 | 86 | 최신, 작고 안전함 |
서명 길이만 봐도 RS256(342자)이 ES256(86자)의 네 배에 가깝습니다.
공개키 크기도 차이가 큰데, PEM으로 뽑아보면 RSA-2048 공개키는 9줄인 반면 EC P-256 공개키는 4줄로 절반 이하입니다.
성능에는 한 가지 재미있는 비대칭성이 있습니다. RSA는 서명은 느리지만 검증은 빠른 특성이 있어서, “한 번 서명하고 여러 번 검증하는” 토큰 사용 패턴에는 의외로 잘 맞습니다. 반대로 ECDSA 계열은 서명과 검증 부하가 비교적 고른 편이고요. 어느 쪽이든 요즘 하드웨어에서는 토큰 한 건당 1밀리초도 안 걸리므로, 대규모 트래픽이 아니라면 성능보다 키 배포 구조와 토큰 크기를 기준으로 고르는 게 현실적입니다.
alg를 반드시 고정하세요
알고리즘 이야기에서 빼놓을 수 없는 보안 주의 사항이 있습니다.
검증할 때 허용할 alg를 명시적으로 못 박아야 합니다.
토큰의 헤더에는 alg 값이 들어 있는데, 검증 로직이 이 값을 그대로 믿어버리면 두 가지 공격에 노출됩니다.
하나는 alg: none을 넣어 서명 검증 자체를 건너뛰게 만드는 공격이고, 다른 하나는 RS256으로 검증할 줄 알았던 서버에 HS256 토큰을 보내 공개키를 HMAC 비밀키로 악용하는 알고리즘 혼동(algorithm confusion) 공격입니다.
그래서 라이브러리에 “이 키로 이 알고리즘만 허용한다”고 명시해야 합니다.
import { jwtVerify } from "jose";
// 허용 알고리즘을 RS256으로 못 박는다
const { payload } = await jwtVerify(token, publicKey, {
algorithms: ["RS256"],
});
마치며
지금까지 JWT 서명에 쓰이는 HS256·RS256·ES256·EdDSA를 비교했습니다.
정리하면, 단일 서비스 내부라면 HS256, 공개키를 배포해야 한다면 RS256(호환성)이나 ES256(효율), 새 프로젝트라면 EdDSA나 ES256이 무난한 선택입니다.
그리고 어떤 알고리즘을 쓰든 검증 시 alg를 반드시 고정해야 한다는 점을 잊지 마세요.
서명이 내부에서 어떻게 동작하는지 더 알고 싶다면 JWS로 이해하는 JSON 데이터 서명을, 공개키를 어떻게 배포하고 교체하는지는 JWK와 JWKS 이해하기를 참고하세요.
각 알고리즘의 정확한 식별자와 파라미터는 RFC 7518 - JSON Web Algorithms에 정리되어 있습니다.
This work is licensed under
CC BY 4.0