ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

HS256

도입

HS256은 JWT/JWS에서 가장 널리 보이는 대칭키 기반 서명 알고리즘 중 하나로, 공유 비밀키 하나로 무결성과 발급자 진위를 검증하는 방식이다.

JWT를 처음 접할 때 HS256은 단순히 “기본 알고리즘” 정도로 보이기 쉽습니다. 하지만 실제로는 서명 방식, 키 배포 방식, 검증 주체의 신뢰 경계, 운영 중 키 관리 방식까지 함께 결정하는 중요한 선택입니다.

특히 HS256은 RSA나 ECDSA처럼 공개키/개인키를 분리하는 구조가 아니라, 같은 비밀키를 알고 있는 주체가 서명도 만들고 검증도 할 수 있다는 점에서 성격이 분명합니다.

그래서 HS256을 이해한다는 것은 단순히 JWT 헤더의 "alg": "HS256" 를 읽는 일이 아니라, JWT의 신뢰 모델을 어떻게 설계할지 이해하는 일에 가깝습니다.

필요성

토큰 내용이 중간에 바뀌지 않았는지와, 그 토큰을 만들 수 있는 비밀을 누가 갖고 있는지가 중요할 때 HS256 같은 keyed MAC 방식이 자연스럽게 등장한다

JWT는 클레임을 전송하는 형식일 뿐이고, 실제 신뢰는 서명 또는 MAC이 제공합니다. HS256은 JWS 계열에서 그 MAC 역할을 하는 가장 대표적인 알고리즘입니다.

장점은 구조가 비교적 단순하다는 점입니다. 공개키 배포 없이도 공유 비밀만 있으면 생성과 검증이 가능합니다. 대신 이 단순함은 곧 키 관리 부담으로 돌아옵니다. 검증할 수 있는 주체는 같은 키로 유효한 MAC도 만들 수 있기 때문입니다.

즉 HS256의 가치는 “쉽다”에만 있지 않고, 신뢰 경계 안에서 빠르고 단순하게 MAC 기반 토큰 검증을 운영할 수 있다는 데 있습니다.

HS256이 특히 잘 맞는 상황
  • 발급자와 검증자가 같은 운영 경계 안에서 하나의 공유 비밀을 안전하게 관리할 수 있을 때
  • JWT에 암호화가 아니라 무결성과 발급자 검증이 필요한 경우
  • 공개키 배포보다 대칭키 운영이 더 단순한 환경일 때
  • 서명보다 keyed MAC 기반 검증이 더 잘 맞는 내부 토큰 모델을 쓸 때

정의

HS256은 JWA에서 정의한 “HMAC using SHA-256” 알고리즘이며, JWS에서 공유 키로 계산한 HMAC 값을 서명 값으로 사용한다

JOSE/JWA 기준에서 HS256 은 “HMAC using SHA-256”을 뜻합니다. 즉 비대칭 서명이 아니라 HMAC 계열의 MAC 알고리즘입니다.

JWT가 JWS Compact Serialization으로 표현될 때는 보통 세 부분, 즉 헤더·페이로드·서명으로 구성되며, 헤더에는 사용한 알고리즘을 나타내는 "alg" 값이 들어갑니다.

예를 들어 JWT/JWS 헤더에서 {"typ":"JWT","alg":"HS256"} 는 이 토큰의 서명 또는 MAC이 HS256으로 계산되었음을 뜻합니다.

핵심 메시지

"HS256의 본질은 JWT를 암호화하는 데 있지 않고

공유 비밀키 기반 MAC으로 토큰의 무결성과 발급자 측 비밀 보유 여부를 검증하는 데 있습니다."

핵심 원리

HS256은 헤더와 페이로드를 base64url로 인코딩한 뒤 점(.)으로 이어 만든 JWS Signing Input에 대해 HMAC-SHA-256을 계산하고, 그 결과를 다시 base64url 인코딩해 서명 부분으로 붙인다

JWS Compact Serialization은 크게 세 부분입니다. BASE64URL(UTF8(Protected Header)), BASE64URL(Payload), BASE64URL(Signature) 가 점으로 연결됩니다.

HS256에서 실제로 HMAC을 계산하는 입력은 앞의 두 부분을 점으로 이어 붙인 문자열입니다. 즉 헤더와 페이로드의 인코딩 결과가 그대로 HMAC의 입력이 됩니다.

그리고 계산된 HMAC 값을 base64url 인코딩한 것이 세 번째 부분, 즉 서명 영역이 됩니다. 검증할 때는 같은 입력과 같은 공유 비밀로 다시 HMAC을 계산해 비교합니다.

JWS Compact Serialization
BASE64URL(UTF8(JWS Protected Header))
.
BASE64URL(JWS Payload)
.
BASE64URL(JWS Signature)

HS256 Signature
BASE64URL(
  HMAC_SHA256(
    shared_secret,
    BASE64URL(UTF8(header)) + "." + BASE64URL(payload)
  )
)

구성 요소

HS256을 제대로 이해하려면 헤더, 페이로드, signing input, 공유 비밀키, MAC 비교 규칙을 함께 봐야 한다
요소 역할 실무 해석
JOSE Header 알고리즘과 부가 메타데이터 명시 alg 는 반드시 있어야 하고 실제 사용 알고리즘과 일치해야 함
Payload 클레임 또는 임의의 내용 JWT에서는 보통 claims set JSON
JWS Signing Input 서명/MAC 계산 대상 base64url(header) + "." + base64url(payload)
Shared Secret HMAC 계산에 쓰는 대칭키 검증자도 같은 키를 알아야 하므로 키 분배와 저장이 핵심
Signature HMAC 결과의 base64url 표현 검증 시 constant-time 비교가 필요

기본 구현

HS256을 가장 빨리 이해하는 방법은 JWT 헤더 예시와 서명 계산 공식을 같이 보는 것이다
{
  "typ": "JWT",
  "alg": "HS256"
}
signingInput =
  BASE64URL(UTF8(header)) + "." + BASE64URL(payload)

signature =
  BASE64URL(HMAC_SHA256(shared_secret, signingInput))

jwt =
  BASE64URL(UTF8(header)) + "." +
  BASE64URL(payload) + "." +
  signature
실전 포인트
여기서 가장 중요한 점은 HS256이 헤더와 페이로드를 “암호화”하는 것이 아니라, 그 둘의 인코딩 결과에 대해 HMAC을 계산한다는 점입니다. 즉 헤더와 페이로드는 base64url 인코딩일 뿐, 누구나 디코딩해서 읽을 수 있습니다.

패턴 1. 대칭키 MAC 모델

HS256은 대칭키 기반 MAC 이므로, 검증자가 같은 비밀을 안다는 사실 자체가 곧 토큰 생성 능력과도 연결된다

HS256은 RFC 7518이 말하듯 “shared key”를 사용합니다. 즉 발급자와 검증자가 같은 비밀을 알고 있어야 합니다.

이 말은 곧, 그 비밀을 가진 주체는 검증뿐 아니라 같은 방식의 유효한 MAC도 만들 수 있다는 뜻입니다. 따라서 공개키 기반 서명처럼 “검증은 많이, 서명은 소수만”이라는 분리를 그대로 기대하면 안 됩니다.

즉 HS256은 단순하고 빠르지만, 검증 주체를 넓게 퍼뜨릴수록 키 관리와 신뢰 경계 설계가 더 중요해집니다.

패턴 2. 키 길이와 키 엔트로피가 핵심이다

HS256의 실제 보안성은 알고리즘 이름보다도, 공유 비밀키가 충분히 길고 무작위성이 높은가에 훨씬 더 크게 좌우된다

JWA는 HS256에 대해 해시 출력 크기와 같은 크기 이상, 즉 최소 256비트 이상의 키를 요구합니다. 따라서 사람이 적당히 정한 짧은 문자열은 설계부터 잘못된 경우가 많습니다.

JWT BCP는 더 강하게 말합니다. 사람이 기억하는 비밀번호 같은 낮은 엔트로피 값은 HS256 키로 직접 사용하면 안 되며, 이런 키는 토큰을 입수한 공격자에게 오프라인 brute-force 또는 dictionary attack 표적이 될 수 있습니다.

즉 HS256에서 “비밀”은 이름만 비밀이어서는 안 되고, 충분히 긴 무작위 바이트열이어야 합니다.

Good Secret
- 256 bits or more
- cryptographically random
- stored securely
- rotated deliberately

Bad Secret
- short config string
- reused password
- human-memorable phrase
- copied across too many services and environments

패턴 3. 알고리즘 검증을 토큰 헤더에 맡기면 안 된다

HS256 자체보다 더 자주 사고를 만드는 것은, 토큰이 들고 온 alg 값을 그대로 믿고 검증 로직을 바꾸는 구현이다

RFC 8725는 라이브러리가 호출자에게 허용 알고리즘 집합을 지정하게 해야 하고, 그 외 알고리즘은 쓰지 말아야 한다고 요구합니다. 즉 토큰 헤더에 적힌 alg 를 그대로 신뢰해 검증 방식을 바꾸면 안 됩니다.

같은 문서는 실제 위협 사례로 none 으로 바꾸는 공격과, RS256HS256 으로 바꾸어 공개키를 HMAC shared secret처럼 오용하게 만드는 공격을 직접 언급합니다.

또 각 키는 정확히 하나의 알고리즘과만 연결되어야 하며, 이 점도 검증 시점에 강제되어야 합니다. 즉 HS256 검증 키와 RS256 검증 키를 같은 흐름에서 느슨하게 섞어 쓰면 안 됩니다.

핵심 포인트
HS256을 안전하게 쓰는 핵심은 “토큰에 HS256이 적혀 있으니 HS256로 검증한다”가 아닙니다. 반대로 애플리케이션이 미리 “이 종류의 토큰은 오직 HS256만 허용한다”고 고정해 둔 뒤, 토큰 헤더가 그 기대와 일치하는지 확인해야 합니다.

한계와 주의점

HS256은 단순하고 강력하지만, 대칭키라는 특성 때문에 서명 권한과 검증 권한을 분리하기 어렵고, JWT payload 자체를 숨겨 주지도 않는다

첫째, HS256은 MAC이지 암호화가 아닙니다. 따라서 JWT가 HS256으로 보호되어 있어도 payload 내용은 base64url로 디코딩해 볼 수 있으며, 기밀성이 필요하면 JWE 같은 별도 암호화 계층을 고려해야 합니다.

둘째, 공유 비밀 하나로 생성과 검증이 모두 가능하므로, 검증 가능한 주체를 넓게 퍼뜨릴수록 비밀 유출과 오남용 리스크도 함께 커집니다.

셋째, HS256 서명이 유효하다고 해서 토큰을 그대로 신뢰해도 되는 것은 아닙니다. JWT BCP는 iss, aud 같은 문맥별 검증과, 서로 다른 종류의 JWT에 대해 상호 배타적인 validation rule을 둘 것을 요구합니다.

주의해야 할 지점
  • HS256은 무결성과 인증용 MAC 이지, payload 암호화가 아님
  • 공유 비밀을 아는 주체는 검증뿐 아니라 유효한 MAC 생성도 가능함
  • 짧거나 예측 가능한 비밀은 오프라인 brute-force 공격에 취약함
  • 서명이 유효해도 iss/aud/type 같은 문맥 검증이 빠지면 토큰 오용이 가능함

자주 하는 실수

HS256을 위험하게 만드는 가장 흔한 원인은 알고리즘 자체보다도, 키 관리와 검증 규칙을 너무 느슨하게 두는 구현이다
  • 사람이 기억할 수 있는 비밀번호나 짧은 문자열을 비밀키로 사용함
  • 토큰의 alg 값을 그대로 믿고 검증 알고리즘을 동적으로 바꿈
  • 같은 키를 여러 알고리즘이나 여러 종류의 JWT에 섞어 사용함
  • HS256 서명이 맞으면 claims 검증도 끝난 것으로 착각함
  • payload 가 서명되었으니 비밀 정보도 안전하다고 오해함
  • 직접 구현하면서 HMAC 비교를 일반 문자열 비교로 처리함

실무 루틴

HS256을 실무에서 안전하게 쓰려면 알고리즘 선택보다 먼저 키 길이·엔트로피·허용 알고리즘 고정·문맥별 클레임 검증을 함께 설계해야 한다
  1. 비밀키는 최소 256비트 이상 의 무작위 바이트열로 생성한다.
  2. 사람이 기억하는 비밀번호를 HS256 키로 직접 쓰지 않는다.
  3. 토큰 종류별로 허용 알고리즘 집합 을 미리 고정하고, alg 가 기대값과 일치하는지 본다.
  4. 키 하나는 가능하면 정확히 하나의 알고리즘 에만 연결한다.
  5. 서명 검증 후에도 iss, aud, typ, 토큰 프로필별 필수 클레임을 따로 검증한다.
  6. 검증 주체를 넓게 퍼뜨릴수록 키 노출 면적이 커진다는 점을 항상 의식한다.

디버깅

HS256 검증이 실패할 때는 토큰 내용보다 먼저 signing input, base64url 처리, 허용 알고리즘, 키 자체가 맞는지부터 보는 편이 훨씬 빠르다
1
먼저 애플리케이션이 HS256을 허용하도록 고정되어 있는지 확인한다.
2
헤더의 alg 값이 실제 기대하는 알고리즘과 일치하는지 본다.
3
같은 shared secret 을 정말 쓰고 있는지, 환경별 비밀이 섞이지 않았는지 확인한다.
4
header.payload 서명을 만들 때 base64url 과 점(.) 연결 규칙이 정확한지 본다.
5
서명 검증이 통과해도 iss, aud, typ, exp 같은 애플리케이션 문맥 검증이 빠지지 않았는지 확인한다.
점검 체크리스트
- 허용 알고리즘이 HS256으로 고정되어 있는가
- alg 헤더와 실제 검증 알고리즘이 일치하는가
- shared secret 이 환경별로 섞이지 않았는가
- header.payload 계산과 base64url 규칙이 정확한가
- 키 길이와 엔트로피가 충분한가
- 서명 외 claims 검증까지 하고 있는가

요약

HS256의 핵심은 공유 비밀키로 HMAC-SHA-256을 계산해 JWT/JWS의 무결성과 발급자 측 비밀 보유 여부를 확인하는 데 있으며, 안전성은 알고리즘 이름보다도 키 엔트로피와 알고리즘 검증 규칙과 토큰 문맥 검증에 더 크게 좌우된다
  • ✅ HS256은 JWA에서 정의한 HMAC using SHA-256 이다.
  • ✅ JWT/JWS에서는 header.payload 에 대해 HMAC-SHA-256을 계산한 뒤 base64url 인코딩한 값이 서명 부분이 된다.
  • ✅ HS256은 서명이 아니라 대칭키 기반 MAC 이다.
  • ✅ 공유 비밀키는 최소 256비트 이상 이어야 하고 충분한 엔트로피를 가져야 한다.
  • ✅ 사람이 기억하는 비밀번호를 HS256 키로 직접 쓰면 안 된다.
  • ✅ 서명 비교는 constant-time 으로 해야 한다.
  • ✅ 토큰의 alg 헤더를 맹신하지 말고 허용 알고리즘을 미리 고정해야 한다.
  • ✅ HS256이 유효하더라도 iss, aud, typ 같은 애플리케이션 문맥 검증은 별도로 필요하다.

 

 

728x90