토스페이먼츠와 카카오페이 등 한국에서 널리 쓰이는 결제 서비스는 웹훅 알림을 HMAC-SHA256으로 서명합니다: 가맹점은 연동 설정 시 비밀 키를 받고, 들어오는 각 알림에는 로컬에서 다시 계산해 비교해야 하는 서명이 함께 옵니다. 서명이 일치하지 않으면 그 알림은 실제로 결제 서비스에서 온 것이 아니며, 진짜 결제 확인으로 처리해서는 안 됩니다.
단순 해시가 아니라 비밀 키가 필요한 이유
SHA-256 같은 일반 해시는 데이터가 전송 중 손상되지 않았다는 것만 증명할 뿐, 누가 보냈는지는 증명하지 못합니다. 서버가 웹훅 본문을 해시화해 요청에 포함된 해시와 비교하기만 한다면, 공격자는 자신만의 가짜 페이로드를 만들고 일치하는 해시를 붙일 수 있습니다. HMAC은 발신자와 수신자만 아는 비밀 키를 섞어 넣어 이 문제를 해결합니다 — 그 키가 없으면, 공격자가 어떤 해시 함수가 쓰이는지 정확히 알고 있어도 유효한 서명을 만드는 것은 사실상 불가능합니다.
이것이 단순히 "키의 해시 더하기 메시지"가 아닌 이유
해싱 전에 키와 메시지를 단순히 이어붙이는 방식(예: hash(키 + 메시지))은 일부 해시 함수에서 길이 확장 공격에 취약합니다 — 공격자가 전체 키를 몰라도 데이터를 추가해 새로운 유효한 해시를 계산할 수 있는 경우가 있습니다. HMAC은 이중 해싱과 내부/외부 패딩을 사용하는 특정 구조로 이런 유형의 공격을 완전히 차단합니다.
HMAC이 등장하는 다른 곳
웹훅 서명 외에도 HMAC은 API 요청을 인증하며, 인증 앱의 6자리 코드를 생성하는 TOTP 알고리즘의 핵심이기도 합니다.
왜 필요한가
- 결제 서비스나 다른 서드파티 서비스에서 온 웹훅의 진위를 검증하기.
- 비밀 키 자체를 네트워크로 전혀 보내지 않고 API 요청에 서명하기.
- TOTP 코드와 세션 토큰의 내부 작동 원리를 이해하기.
상수 시간 비교
수신한 서명을 예상값과 일반 === 연산자로 비교하는 것은 안전하지 않습니다. 이런 연산자는 보통 첫 번째로 다른 바이트에서 비교를 멈추기 때문에, 비교에 걸리는 시간이 공격자에게 앞부분 몇 바이트를 올바르게 맞혔는지 알려주게 됩니다. 제대로 구현된 웹훅 핸들러는 항상 모든 바이트를 첫 번째 불일치 위치와 관계없이 검사하는 상수 시간 비교 함수(예: PHP의 hash_equals나 Node.js의 crypto.timingSafeEqual)를 사용합니다.