HMAC SHA256 서명 검증 방법, 값으로 확인

웹훅이나 API 응답에 담겨 온 HMAC 서명값이 직접 계산한 값과 다르게 나오면, 대부분은 보안 침해가 아니라 메시지·비밀키·해시 알고리즘 세 입력 중 하나가 미세하게 다르기 때문입니다. 이 세 입력이 정확히 같아야만 같은 서명값이 나옵니다.

이 글은 실제로 계산한 값을 예시로 보여주면서, 서명이 안 맞을 때 무엇부터 확인해야 하는지를 정리합니다. 전문가 판단이 필요한 문제가 아니라, 같은 입력을 다시 넣어 같은 값이 나오는지 눈으로 비교하면 끝나는 절차입니다.

요약 ① HMAC 서명값은 메시지·비밀키·해시 알고리즘 세 가지 입력으로 계산되며, 이 셋이 정확히 같아야 같은 값이 나옵니다. ② 값이 안 맞는 원인은 대부분 공백·줄바꿈, 대소문자, 인코딩(16진수/Base64), 알고리즘 선택 차이입니다. ③ 검증은 같은 메시지·키·알고리즘으로 직접 다시 계산해, 나온 값을 문자 단위로 비교하는 것으로 이뤄집니다.

웹훅 서명이 안 맞을 때 흔히 벌어지는 일

외부 서비스가 결제 완료나 주문 상태 변경 같은 이벤트를 알리는 웹훅을 보낼 때, 요청 본문(payload)과 함께 그 본문으로 계산한 HMAC 서명값을 헤더에 실어 보내는 방식이 흔히 쓰입니다. 수신 측은 같은 비밀키로 자신도 서명을 계산해, 헤더로 받은 값과 일치하는지 비교합니다.

이 비교가 실패하면 코드에 오류가 있거나 요청이 위조됐다고 의심하기 쉽습니다. 그런데 실제로는 원문 payload를 그대로 쓰지 않고, 한 번 파싱했다가 다시 문자열로 만드는 과정에서 공백이 하나 더 들어가거나 줄바꿈이 붙는 경우가 흔합니다.

HMAC은 이런 미세한 차이에 의도적으로 민감하게 반응하도록 설계되어 있습니다. 메시지 한 글자, 비밀키의 대소문자 하나, 선택한 해시 알고리즘 하나만 달라도 결과값은 처음부터 끝까지 전혀 다른 문자열이 됩니다.

도구 없이 직접 HMAC 값을 계산해보는 방법

HMAC은 RFC 2104 문서가 정의한 표준화된 계산 방식으로, 비밀키와 해시 알고리즘을 이용해 메시지에 서명을 붙입니다. 이 계산은 특정 프로그램 없이도 터미널에서 바로 재현할 수 있습니다.

macOS와 리눅스에는 OpenSSL이 기본으로 설치돼 있어, 다음 명령으로 같은 계산을 할 수 있습니다.

echo -n "order:1001,amount:5000" | openssl dgst -sha256 -hmac "mySecretKey"

이 명령을 실행하면 다음 결과가 나옵니다.

SHA2-256(stdin)= 148bec9dd63667edc6b3d37f9e7f5607c75144bd267159d843ec898b0667cea4

메시지는 order:1001,amount:5000, 비밀키는 mySecretKey, 알고리즘은 SHA-256을 썼을 때 나오는 값입니다. 같은 세 입력을 넣으면 운영체제나 프로그래밍 언어와 상관없이 항상 이 값이 나옵니다.

다른 언어에서도 같은 값이 나오는지 확인하기

실제로 Python의 hmac 모듈로 같은 메시지와 키를 계산해도 동일한 값이 나오는 것을 확인했습니다.

import hmac, hashlib
hmac.new(b"mySecretKey", b"order:1001,amount:5000", hashlib.sha256).hexdigest()

echo에서 -n 옵션을 빼먹으면 메시지 끝에 줄바꿈 문자가 자동으로 붙어, 같은 메시지·키·알고리즘이어도 전혀 다른 값이 나옵니다. 이 문제는 바로 다음 항목에서 실제 값으로 다시 보여드립니다.

저희 도구로 직접 확인해보겠습니다.

HMAC 해시 서명 생성기

브라우저 안에서 Web Crypto API로 즉시 계산되며, 입력한 메시지와 비밀키는 서버로 전송되지 않습니다.

서명이 안 맞을 때 흔히 놓치는 4가지

같은 메시지 order:1001,amount:5000과 같은 비밀키 mySecretKey로 SHA-256 서명값을 계산하되, 아주 작은 부분 하나씩만 바꿔보면 다음과 같은 결과가 나옵니다.

원본:            order:1001,amount:5000
SHA-256 결과:     148bec9dd63667edc6b3d37f9e7f5607c75144bd267159d843ec898b0667cea4

끝에 줄바꿈 추가:  order:1001,amount:5000  (뒤에 개행문자 1개)
SHA-256 결과:     015bc2c0bf5cc82fd005e37b7a2b0ae8777e41a109bae303b263aaa162f32cff

첫 글자만 대문자로: Order:1001,amount:5000
SHA-256 결과:     11c1403b448d39601f4a14119514f6a44b16bdcb378f384d767ffafc0054ffb2

메시지 자체가 미세하게 다른 경우

메시지는 눈으로 보기에 거의 같지만 결과값은 앞자리부터 완전히 다릅니다. 이것이 "서명이 안 맞는다"는 문제의 실체인 경우가 많습니다. 비밀키가 유출됐거나 로직이 잘못된 게 아니라, 비교하는 두 문자열 자체가 실제로는 다른 경우입니다.

인코딩과 알고리즘이 다른 경우

같은 서명이라도 표현 형식이 다르면 문자열이 달라 보입니다. 위 원본 예시의 결과값을 Base64로 표현하면 FIvsndY2Z+3Gs9N/nn9WB8dRRL0mcVnYQ+yJiwZnzqQ=가 되는데, 이는 같은 서명을 16진수 대신 Base64로 표기한 것뿐입니다.

헤더로 받은 값은 16진수인데 내 쪽은 Base64로 계산해 비교하면 값이 다르게 보이는 게 당연합니다.

알고리즘 자체가 다르면 값의 길이부터 다릅니다. 같은 메시지·키라도 SHA-1은 40자, SHA-256은 64자, SHA-384는 96자, SHA-512는 128자의 16진수 문자열이 나옵니다.

위 메시지를 SHA-1로 계산하면 d67244f34d44c077a2a3dada4528c8284c2951cb로, SHA-256과는 길이도 값도 다릅니다. 네 가지 원인을 정리하면 다음과 같습니다.

원인증상확인 방법
메시지 끝 공백·줄바꿈원문이 같아 보이는데 값이 다름메시지 끝에 스페이스·개행이 있는지 직접 확인
대소문자 차이필드명·값 일부만 다름원본 payload를 그대로 복사했는지 문자 단위로 대조
인코딩 차이(16진수/Base64)값 길이·구성 문자가 다름(Base64는 +, /, = 포함)비교 대상 서명이 16진수인지 Base64인지 먼저 확인
알고리즘 불일치값의 길이 자체가 다름(40/64/96/128자)발신 측이 명시한 알고리즘과 선택한 알고리즘이 같은지 확인

정리

  1. HMAC 서명값은 메시지·비밀키·알고리즘 세 입력으로 결정되며, 셋이 모두 같아야 같은 값이 나옵니다.
  2. 메시지에 공백이나 줄바꿈이 하나만 더 있어도 결과값은 완전히 달라집니다.
  3. 값의 길이가 서로 다르면 십중팔구 인코딩(16진수/Base64) 또는 알고리즘이 다른 경우입니다.
  4. 검증은 같은 세 입력을 넣어 다시 계산한 값과, 받은 값을 문자 단위로 비교하는 것으로 충분합니다.
  5. openssl dgst 명령이나 브라우저 도구로 직접 재계산해보면 어느 입력이 다른지 좁혀나갈 수 있습니다.
HMAC 해시 서명 생성기

브라우저 안에서 Web Crypto API로 즉시 계산되며, 입력한 메시지와 비밀키는 서버로 전송되지 않습니다.

자주 묻는 질문

같은 메시지와 키인데 값이 매번 다르게 나옵니다. 정상인가요?

아니요. HMAC은 메시지·비밀키·알고리즘이 같으면 언제 어디서 계산하든 항상 같은 값이 나오는 결정적(deterministic) 계산입니다. 값이 계속 바뀐다면 셋 중 하나가 실제로 바뀌고 있다는 뜻입니다. 메시지에 현재 시각처럼 매번 달라지는 값이 섞여 있는 경우가 흔합니다.

16진수와 Base64 중 어느 형식이 맞는 건가요?

둘 다 같은 결과 바이트를 표현하는 방식일 뿐, 어느 하나가 정답은 아닙니다. 서명을 보낸 쪽이 어떤 형식으로 인코딩했는지에 맞춰야 값이 일치합니다. 값이 0-9, a-f로만 구성돼 있으면 16진수, +, /, = 문자가 섞여 있으면 Base64입니다.

알고리즘을 모를 때는 어떻게 알아내나요?

메시지·키를 그대로 둔 채 SHA-256, SHA-384, SHA-512, SHA-1 순으로 각각 계산해보고, 받은 값과 길이가 같은 알고리즘부터 대조하면 됩니다. 문서나 API 명세에 알고리즘이 명시된 경우가 많으므로 먼저 확인하는 것이 정확합니다.

메시지에 한글이나 특수문자가 있으면 값이 달라지나요?

인코딩 방식이 같다면(예: UTF-8) 같은 문자열은 항상 같은 바이트로 변환되어 같은 값이 나옵니다. 다만 저장·전달 과정에서 문자 인코딩이 달라지면(예: 한쪽은 UTF-8, 다른 쪽은 EUC-KR) 같은 글자처럼 보여도 실제 바이트가 달라져 결과값이 달라질 수 있습니다.

가격 보기카톡 무료 상담