การตรวจสอบ Signature ของ BentoWeb Webhook ด้วย HMAC-SHA256
🔐 การตรวจสอบ Signature ของ BentoWeb Webhook ด้วย HMAC-SHA256
เมื่อคุณรับ Webhook จาก BentoWeb ระบบจะส่งข้อมูลมาพร้อมกับ Signature เพื่อให้คุณสามารถตรวจสอบความถูกต้องของข้อมูล และป้องกันการปลอมแปลงได้
📬 ข้อมูลที่คุณจะได้รับจาก Webhook
Body: JSON Payload
Header:
Authorization: Signature สำหรับตรวจสอบ
🔑 หลักการสร้าง Signature
Signature ถูกสร้างจาก:
HMAC_SHA256(client_id + json_payload, client_secret)
โดย
client_id คือรหัสประจำระบบของคุณ
json_payload คือข้อมูลใน body (ต้อง normalize ก่อน hash)
client_secret คือรหัสลับที่คุณได้รับจาก BentoWeb
⚠️ จุดสำคัญที่ทำให้ Signature ไม่ตรง
JSON ที่รับมามีการ escape unicode (\u0e1e) ซึ่งต้องแปลงกลับก่อน
ช่องว่างหรือลำดับ key เปลี่ยนไปตอนทำ json_encode() ใหม่
ไม่ได้ใช้ UTF-8 encoding ก่อน hash
ใช้ hash ผิดประเภท (ต้องใช้ HMAC-SHA256 เท่านั้น)
Authorization ไม่มีการส่งค่า โปรดติดต่อเจ้าหน้าที่ เพื่อระบุ ID ร้านลงในตัว Client ID ที่คุณใช้งานอยู่
✅ ขั้นตอนการตรวจสอบ Signature
รับ Authorization จาก header
รับ raw body JSON
json_decode() เป็น object/array
json_encode() กลับแบบไม่ escape:
ต้องใช้ JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES (หรือเทียบเท่า)
รวม client_id + json
สร้าง HMAC-SHA256 ด้วย client_secret
เปรียบเทียบกับ Signature ที่ส่งมา
🔧 ตัวอย่างโค้ดในหลายภาษา
🐘 PHP
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$baseString = 'your_client_id' . $json;
$expected = hash_hmac('sha256', $baseString, 'your_client_secret');
$provided = $_SERVER['HTTP_AUTHORIZATION'];
if (!hash_equals($expected, $provided)) {
http_response_code(403);
exit('Signature mismatch');
}
🐍 Python
import json, hmac, hashlib
from flask import request
client_id = "your_client_id"
client_secret = b"your_client_secret"
raw = request.get_data(as_text=True)
data = json.loads(raw)
json_string = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
base_string = client_id + json_string
expected_signature = hmac.new(client_secret, base_string.encode('utf-8'), hashlib.sha256).hexdigest()
if request.headers.get('Authorization') != expected_signature:
return "Signature mismatch", 403
🌐 Node.js
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.json({ verify: (req, res, buf) => req.rawBody = buf }));
app.post('/webhook', (req, res) => {
const clientId = "your_client_id";
const clientSecret = "your_client_secret";
const json = JSON.stringify(req.body);
const baseString = clientId + json;
const expectedSignature = crypto.createHmac('sha256', clientSecret)
.update(baseString, 'utf8')
.digest('hex');
const providedSignature = req.get('Authorization');
if (providedSignature !== expectedSignature) {
return res.status(403).send('Signature mismatch');
}
res.send('OK');
});
🦫 Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
clientId := "your_client_id"
clientSecret := "your_client_secret"
body, _ := ioutil.ReadAll(r.Body)
var payload map[string]interface{}
json.Unmarshal(body, &payload)
jsonBytes, _ := json.Marshal(payload)
baseString := clientId + string(jsonBytes)
mac := hmac.New(sha256.New, []byte(clientSecret))
mac.Write([]byte(baseString))
expectedSignature := hex.EncodeToString(mac.Sum(nil))
providedSignature := r.Header.Get("Authorization")
if providedSignature != expectedSignature {
http.Error(w, "Signature mismatch", http.StatusForbidden)
return
}
w.Write([]byte("OK"))
}
🧪 ทดสอบลายเซ็นไม่ตรง
หากคุณทดสอบแล้วลายเซ็นไม่ตรง ให้ตรวจสอบว่า:
JSON ถูก encode ซ้ำหรือ escape ไม่ตรง (ไม่ escape unicode, ไม่มีช่องว่าง)
ตรวจว่าใช้ HMAC-SHA256
ตรวจว่าใช้ UTF-8 encoding
client_id หรือ client_secret ไม่ถูกต้อง
มีการแก้ไข body หลังจาก verify แล้ว
มี request จากภายนอกที่ไม่ใช่ BentoWeb
อัปเดตเมื่อ: 26/05/2025
ขอบคุณ!