HMAC 생성기

비밀키와 메시지로 HMAC(SHA-256 등) 서명 값을 생성합니다.

출력 형식
HMAC 결과

HMAC은 브라우저 내장 crypto.subtle(Web Crypto)로만 계산됩니다. 메시지와 비밀키는 서버로 전송되지 않으며, 비밀키는 브라우저에 저장하지 않습니다 — 새로고침하면 사라집니다.

SHA-1 HMAC은 레거시 호환용입니다. 새로 설계한다면 SHA-256 이상을 쓰세요. 서버에서 서명을 검증할 때는 문자열을 == 로 비교하지 말고 타이밍 안전 비교(hash_equals, crypto.timingSafeEqual 등)를 사용하세요.

HMAC 생성기란?

비밀키와 메시지로 HMAC(해시 기반 메시지 인증 코드) 서명을 생성하거나 검증하는 온라인 도구입니다. API 요청 서명, 웹훅(Webhook) 페이로드 검증 등 메시지의 무결성과 진위를 동시에 확인해야 할 때 HMAC이 사용됩니다. 메시지와 비밀키를 붙여넣으면 결과가 실시간으로 계산되며, 생성과 검증 모드를 자유롭게 오갈 수 있습니다. 가장 큰 장점은 보안입니다. 모든 계산은 사용자의 브라우저 안에서만 웹 표준 Crypto API로 이루어지며, 입력한 메시지나 비밀키는 서버로 절대 전송되지 않습니다. 회원가입이나 설치 없이, SHA-256, SHA-512 등 다양한 해시 알고리즘과 텍스트, 16진수(Hex), Base64 등 여러 입출력 형식을 지원하여 어떤 개발 환경의 요구사항에도 유연하게 대응할 수 있습니다.

사용 방법

  1. 상단에서 '🔏 생성' 또는 '✅ 검증' 모드를 선택합니다.
  2. 생성할 원본 '메시지'와 암호화에 쓸 '비밀키'를 각각 입력합니다. 비밀키는 텍스트, 16진수, Base64 형식 중에서 선택할 수 있습니다.
  3. 사용할 '해시 알고리즘'(예: SHA-256)과 '출력 형식'(예: hex)을 선택합니다.
  4. 입력과 동시에 'HMAC 결과' 창에 계산된 서명 값이 나타납니다. '복사' 버튼으로 간편하게 사용하세요.
  5. 검증 모드에서는 '기대값' 입력칸에 비교할 서명을 붙여넣으면 계산 결과와 일치하는지 즉시 확인할 수 있습니다.
  6. '예시 넣기' 버튼을 누르면 모든 입력칸이 예시 값으로 채워져 사용법을 빠르게 익힐 수 있습니다.

HMAC 생성기 사용 가이드

실제 작업에서 이 도구를 어떻게 쓰는지, 무엇을 조심해야 하는지 정리했습니다.

웹훅 서명 검증에서 대부분은 이 실수입니다

결제 완료 웹훅을 받는 서버를 만들면 반드시 서명 검증을 해야 합니다. 안 하면 누구나 "결제 성공" 요청을 흉내 내 상품을 공짜로 가져갈 수 있습니다. 그런데 검증 코드를 처음 짜면 거의 예외 없이 서명이 불일치합니다. 원인은 대개 하나입니다 — 파싱된 객체를 다시 문자열로 만들어 서명하기 때문입니다.

보내는 쪽은 자기가 전송한 본문 바이트 그대로에 서명합니다. 받는 쪽이 JSON으로 파싱한 뒤 다시 문자열로 만들면 공백·줄바꿈·키 순서·한글 이스케이프가 미묘하게 달라지고, 한 바이트만 달라도 HMAC은 완전히 다른 값이 됩니다. 반드시 원본 바이트(raw body)에 서명해야 합니다.

javascript
// Express: 이 라우트만 원본 바이트를 받는다 (전역 express.json() 보다 먼저)
app.post("/webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.get("X-Signature") || "";
    const mine = crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
      .update(req.body)          // Buffer 그대로. 파싱 후 재직렬화 금지
      .digest("hex");

    const a = Buffer.from(sig, "utf8");
    const b = Buffer.from(mine, "utf8");
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));  // 검증에 성공한 뒤에 파싱
    res.sendStatus(200);
  });
전역으로 express.json() 을 걸어두면 그 시점에 원본 바이트가 사라져 서명 검증이 영구히 실패합니다. 웹훅 라우트만 raw로 받도록 순서를 잡으세요. 프레임워크가 무엇이든 원리는 같습니다.

값이 안 맞을 때 점검 순서

위 탭에서 "검증"을 고르면 기대값 칸이 나타납니다. 상대가 보낸 서명을 그대로 붙여넣으면 방금 계산한 값과 비교해 일치·불일치를 알려줍니다. GitHub 스타일의 sha256= 접두어는 자동으로 떼어내고 hex와 Base64를 모두 인식합니다. 불일치라면 아래를 순서대로 보세요.

  • 비밀키 형식 — 같은 키 문자열이라도 텍스트로 읽느냐 16진수로 읽느냐 Base64로 읽느냐에 따라 완전히 다른 바이트가 됩니다. 문서에 "hex로 인코딩된 키"라고 적혀 있으면 반드시 16진수 탭을 고르세요. 이 항목에서 틀리는 경우가 가장 많습니다.
  • 출력 형식 — 상대가 Base64를 기대하는데 hex를 보내면 당연히 안 맞습니다. 길이가 두 배 가까이 차이 나면 이걸 의심하세요.
  • 알고리즘 — SHA-256인지 SHA-1인지. 결과 바이트 수(32 vs 20)로 바로 구분됩니다.
  • 메시지의 앞뒤 공백·줄바꿈 — 복사하다 딸려온 개행 하나로 값이 바뀝니다. CRLF와 LF도 다른 바이트입니다.
  • 인코딩 — 메시지에 한글이 있으면 양쪽 다 UTF-8이어야 합니다. 이 도구는 UTF-8로 인코딩합니다.
  • 서명 대상 — 본문만인지, 타임스탬프와 본문을 특정 형식으로 이어붙인 것인지. 서비스마다 다르니 연동 문서를 보세요.
기대값이 "해석할 수 없습니다"로 나오면 값이 틀린 게 아니라 길이가 안 맞는 것입니다. SHA-256이면 hex 64자 또는 Base64 44자여야 합니다.

왜 == 로 비교하면 안 되나 (타이밍 공격)

문자열 비교는 보통 앞에서부터 한 바이트씩 보다가 다르면 즉시 멈춥니다. 즉 첫 바이트가 틀린 값과 앞 열 바이트가 맞는 값은 비교에 걸리는 시간이 다릅니다. 공격자가 서명을 조금씩 바꿔가며 응답 시간을 수천 번 측정하면, 맞은 바이트가 하나 늘 때마다 아주 미세하게 느려지는 지점을 찾아 한 바이트씩 정답을 알아낼 수 있습니다.

실제 인터넷 너머로 이 차이를 측정하는 것은 네트워크 잡음 때문에 쉽지 않습니다. 하지만 같은 데이터센터 안이거나 요청을 대량으로 반복할 수 있으면 통계적으로 걸러집니다. 무엇보다 방어 비용이 0입니다 — 함수 하나만 바꾸면 됩니다.

언어쓸 함수
Node.jscrypto.timingSafeEqual(a, b) — 길이가 다르면 예외이므로 길이를 먼저 확인
PHPhash_equals($expected, $actual)
Pythonhmac.compare_digest(a, b)
JavaMessageDigest.isEqual(a, b)
Gohmac.Equal(a, b)
이 도구의 검증도 조기 반환 없이 전체 바이트를 XOR로 누적해 비교합니다. 브라우저 안에서 도는 코드라 타이밍 공격 대상은 아니지만, 습관을 그대로 유지하는 편이 낫습니다.

HMAC과 단순 해시는 무엇이 다른가

"비밀키를 앞에 붙이고 해시하면 되는 것 아닌가" — sha256(secret + message) 방식은 실제로 취약합니다. SHA-256을 포함한 여러 해시 함수는 내부 구조상 길이 확장 공격이 가능합니다. 공격자가 secret 값을 몰라도 secret+message 의 해시 값과 message 만 알면, 뒤에 임의의 데이터를 덧붙인 메시지에 대한 올바른 해시를 계산해낼 수 있습니다. 키를 모르는 채로 유효한 서명을 위조하는 셈입니다.

HMAC은 키를 두 번에 나눠 안쪽 해시와 바깥쪽 해시로 감싸는 구조라 이 공격이 통하지 않습니다. 서명 방식을 직접 만들지 말고 표준 HMAC을 쓰라는 말이 나오는 이유입니다.

방식위조 방지평가
sha256(message)없음 — 누구나 계산 가능무결성만. 인증 아님
sha256(secret + message)길이 확장 공격에 취약쓰지 마세요
HMAC-SHA256(secret, message)안전표준. 이 도구의 기본값
SHA-1 선택지는 옛 시스템과 맞춰야 할 때를 위한 것입니다. HMAC-SHA1이 당장 깨진다는 뜻은 아니지만, 새로 설계한다면 이유 없이 SHA-256 아래로 내려갈 까닭이 없습니다.

서명이 맞아도 막지 못하는 것 — 재전송 공격

HMAC이 검증해 주는 것은 "이 메시지가 키를 가진 쪽에서 왔고 도중에 바뀌지 않았다"는 사실뿐입니다. 그 메시지가 언제 만들어졌는지, 이미 처리한 것인지는 알려주지 않습니다. 공격자가 유효한 요청을 그대로 복사해 100번 다시 보내면 서명은 100번 다 통과합니다.

  • 타임스탬프를 서명 대상에 포함하고, 서버 시각과 5분 이상 차이 나는 요청은 거부하세요. 타임스탬프가 서명 밖에 있으면 공격자가 고쳐 쓸 수 있으니 반드시 서명 안에 넣어야 합니다.
  • 이벤트 ID를 기록해 이미 처리한 것은 무시하세요(멱등 처리). 웹훅은 정상적으로도 중복 도착합니다.
  • 금액·주문번호 같은 핵심 값은 서명이 맞더라도 우리 DB의 원장과 대조해 확인하세요.
  • 비밀키와 메시지는 이 브라우저 안에서만 처리되고 서버로 가지 않습니다. 비밀키는 저장조차 하지 않아 새로고침하면 사라집니다. 그래도 운영 키를 웹 도구에 붙여넣는 습관은 권하지 않습니다 — 테스트 키로 형식을 맞춰본 뒤 코드로 옮기세요.

자주 묻는 질문

일반 해시(Hash)와 무엇이 다른가요?

HMAC은 '비밀키'를 아는 사람만 같은 값을 만들 수 있다는 점이 다릅니다. 일반 해시는 데이터의 '무결성'(내용이 같은지)만 검증하지만, HMAC은 비밀키를 통해 '진위'(올바른 송신자가 보냈는지)까지 증명합니다. 이 때문에 단순 데이터 비교가 아닌 웹훅, API 요청 같은 인증이 필요한 곳에 널리 사용됩니다.

입력한 비밀키는 안전하게 처리되나요?

네, 매우 안전합니다. 모든 HMAC 계산은 사용자의 브라우저 내에서만 실행되며, 입력하신 메시지나 비밀키는 저희 서버를 포함한 외부로 일절 전송되지 않습니다. 또한 보안을 위해, 비밀키는 브라우저에 저장되지 않고 페이지를 새로고침하면 즉시 초기화됩니다.

웹훅(Webhook) 서명 검증은 어떻게 하나요?

먼저 '✅ 검증' 모드로 전환하세요. 웹훅 요청 본문(payload) 전체를 '메시지' 칸에 붙여넣고, 서비스 제공자에게 받은 비밀키(secret)를 '비밀키' 칸에 입력합니다. 마지막으로, 요청 헤더(예: `X-Hub-Signature-256`)에 포함된 서명 값을 '기대값' 칸에 넣으면 일치 여부를 즉시 판별할 수 있습니다.

검증 시 '기대값'은 어떤 형식으로 넣어야 하나요?

편리하게 사용하실 수 있도록 여러 형식을 자동으로 인식합니다. 16진수(Hex), Base64, Base64URL 형식의 문자열을 그대로 붙여넣으면 됩니다. 또한 GitHub 등에서 사용하는 'sha256=' 와 같은 접두어가 있어도 알아서 제거하고 비교하므로 신경 쓰지 않으셔도 됩니다.