PacteryDevelopers

서명 검증

웹훅 요청이 진짜 팩터리에서 왔는지, 변조되지 않았는지 Pactery-Signature 헤더로 검증합니다.

헤더 형식

Pactery-Signature: t=1717570000, v1=5a2f8c...
  • t: 서명 생성 시각(epoch 초)
  • v1: {t}.{raw_body}를 웹훅 비밀키(whsec_...)로 HMAC-SHA256 한 값

검증 절차

  1. t와 원문 본문을 .으로 이어 서명 대상 문자열을 만든다.
  2. 비밀키로 HMAC-SHA256을 계산한다.
  3. 결과를 v1과 상수 시간 비교한다.
  4. t가 현재 시각과 5분 이상 차이 나면 재생 공격으로 보고 거절한다.
import crypto from 'crypto'

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map(p => p.trim().split('=')))
  const signed = `${parts.t}.${rawBody}`
  const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex')
  const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
  if (!ok) throw new Error('bad signature')
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) throw new Error('stale')
}

검증은 반드시 **파싱 전 원문(raw body)**으로 해야 합니다. JSON으로 다시 직렬화한 문자열은 바이트가 달라질 수 있습니다.