Bỏ qua để đến nội dung
Beta · Đặc tả có thể còn thay đổi trước khi ra mắt chính thức. Tên miền API api.hensa.example là tạm thời. Xem thay đổi →

Xác thực chữ ký

Ai cũng có thể gửi một POST giả tới URL webhook của bạn và nói “đơn DH-1024 đã thanh toán”. Chữ ký là cách duy nhất để biết request thật sự đến từ Hensa và không bị sửa.

Hensa-Signature: t=1762156992,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t – thời điểm ký (Unix, giây).
  • v1 – HMAC-SHA256(secret, "<t>.<raw body>"), dạng hex. Trong 24 giờ sau khi đổi secret sẽ có hai giá trị v1.
  1. Đọc body thô (bytes) của request – trước khi parse JSON.
  2. Tách t và mọi giá trị v1 từ header Hensa-Signature.
  3. Từ chối nếu t lệch quá 5 phút so với giờ máy chủ (chống phát lại).
  4. Tính HMAC-SHA256(secret, t + "." + body) và so sánh hằng thời gian với từng v1. Khớp một giá trị là hợp lệ.
verify-hensa-signature.js
import crypto from 'node:crypto'
/** Returns true when the request really comes from Hensa and is fresh (≤ 300 s). */
export function verifyHensaSignature(rawBody, header, secret, toleranceSec = 300) {
const parts = (header ?? '').split(',').map((p) => p.trim().split('='))
const t = parts.find(([k]) => k === 't')?.[1]
const signatures = parts.filter(([k]) => k === 'v1').map(([, v]) => v) // 2 signatures while a secret rotates
if (!t || signatures.length === 0) return false
if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false // replay protection
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
return signatures.some((s) =>
s.length === expected.length && crypto.timingSafeEqual(Buffer.from(s, 'hex'), Buffer.from(expected, 'hex'))
)
}
Triệu chứng Nguyên nhân Cách sửa
Chữ ký luôn sai Framework đã parse JSON rồi stringify lại – bytes đã khác Lấy body thô: Express express.raw(), Laravel $request->getContent(), Next.js await req.text()
Chữ ký sai sau khi đổi secret Vẫn dùng secret cũ Cập nhật secret mới; trong 24 giờ hai chữ ký cùng hợp lệ
Lúc đúng lúc sai Đồng hồ máy chủ lệch Bật đồng bộ giờ (NTP)
Dùng == để so sánh Dễ bị tấn công đo thời gian Dùng timingSafeEqual / hash_equals / hmac.compare_digest

Dashboard › Developer › Webhook › Đổi secret (hoặc POST /webhook_endpoints/{id}/rotate_secret). Secret cũ còn hiệu lực 24 giờ; trong thời gian đó mỗi sự kiện mang hai chữ ký v1 – triển khai secret mới lúc nào cũng được, không mất sự kiện nào.