2026-08-08
웹훅 서명 검증(HMAC-SHA256) 구현하는 법
웹훅 서명 검증(HMAC-SHA256) 구현하는 법
결제 웹훅은 외부에서 우리 서버로 들어오는 요청입니다. 누구나 같은 URL로 가짜 요청을 보낼 수 있기 때문에, 발신자가 진짜인지 반드시 검증해야 합니다.
왜 서명 검증이 필요한가
웹훅 URL이 공개되면 악의적인 요청자가 invoice.payment_succeeded 같은 이벤트를 위조해서 보낼 수 있습니다. 서명 검증 없이 이 요청을 그대로 믿고 처리하면, 결제하지 않은 사용자에게 서비스를 제공하는 사고로 이어질 수 있습니다.
HMAC-SHA256 방식
슈퍼빌링은 웹훅 등록 시 발급되는 시크릿으로 요청 본문을 서명해 X-SuperBilling-Signature 헤더에 담아 보냅니다.
import { verifyWebhookSignature } from "@superbilling/sdk-node";
export async function POST(req: Request) {
const rawBody = await req.text(); // 반드시 raw body 문자열을 그대로 사용
const signature = req.headers.get("x-superbilling-signature") ?? "";
if (!verifyWebhookSignature(rawBody, signature, process.env.SUPERBILLING_WEBHOOK_SECRET!)) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
// event.type: "subscription.created" | "invoice.payment_succeeded" |
// "invoice.payment_failed" | "subscription.canceled"
return new Response("ok", { status: 200 });
}자주 하는 실수
- raw body 대신 파싱된 JSON을 서명 대상으로 쓰는 것: 키 순서나 공백이 바뀌면 서명이 항상 실패합니다. 반드시 요청 본문 문자열 그대로를 사용해야 합니다.
- 재시도를 고려하지 않는 것: 전송 실패 시 최대 3회(1초/3초 간격)까지 재시도되므로, 핸들러는 같은 이벤트를 여러 번 받아도 안전하게 처리(idempotent)해야 합니다.
자세한 이벤트 목록과 등록 방법은 Quickstart 문서에서 확인할 수 있습니다.