နားလည်ထားရမယ့် အချက်
သင့် webhook endpoint ဟာ public URL တစ်ခုပါ။ ဒီ URL ကို ရှာတွေ့ ဒါမှမဟုတ် ခန့်မှန်းတွေ့တဲ့ internet ပေါ်က မည်သူမဆို real event ပုံစံတူတူ POST request ကို ပို့နိုင်ပါတယ်။ URL ကိုယ်တိုင်က request တစ်ခုဟာ provider ဆီကနေ တကယ်လာတယ်ဆိုတာကို ဘာမှ သက်သေမပြနိုင်ပါဘူး။
ဖြေရှင်းချက်ကတော့ signature verification ပါ။ Provider က shared secret ကို သုံးပြီး request body အပေါ် HMAC signature တွက်ချက်ပါတယ်။ သင့် server က တူညီတဲ့ HMAC ကို ပြန်တွက်ပြီး နှိုင်းယှဉ်ပါတယ် — ကိုက်ညီရင် accept၊ မကိုက်ညီရင် 401 နဲ့ ချက်ချင်း ငြင်းပယ်ပါ။
- Signature နဲ့ payload ကို လမ်းကြားမှာ မဖတ်နိုင် မပြင်ဆင်နိုင်အောင် HTTPS ကနေသာ webhook တွေကို လက်ခံပါ။
- Replay attack ကို ကာကွယ်ဖို့ request ထဲက timestamp ကို စစ်ပြီး အဟောင်းလွန်းရင် ငြင်းပယ်ဖို့ စဉ်းစားပါ။
Signature verification ဆိုတာ ရွေးချယ်နိုင်တဲ့ hardening မဟုတ်ပါဘူး — internet ပေါ်က မည်သူမဆို သင့် payment provider အဖြစ် ဟန်ဆောင်တာကနေ သင့် endpoint ကို ကာကွယ်ပေးနေတဲ့ တစ်ခုတည်းသော အရာပါပဲ။
- Webhook Signature
- Webhook request တစ်ခုရဲ့ body အပေါ် shared secret ကို သုံးပြီး တွက်ချက်ထားတဲ့ cryptographic hash တစ်ခုဖြစ်ပြီး၊ request ဟာ ပြောင်းလဲထား ဒါမှမဟုတ် ဟန်ဆောင်ထားခြင်း မဟုတ်ကြောင်း လက်ခံသူ အတည်ပြုနိုင်ဖို့ request header ထဲမှာ ပို့ပေးထားတာပါ။
- HMAC
- HMAC (Hash-based Message Authentication Code) ဆိုတာ secret key တစ်ခုနဲ့ message တစ်ခုကို hash function တစ်ခုသုံးပြီး ပေါင်းစပ်တဲ့ နည်းလမ်းတစ်ခုဖြစ်ပြီး၊ key ကို သိသူပဲ အဲဒီ message အတွက် valid signature ကို ဖန်တီးနိုင်ပါတယ်။
WEBHOOK SIGNATURE VERIFICATION FLOW
-----------------------------------
-----
Incoming POST request with body + X-Signature header
|
v
Compute HMAC-SHA256(body, shared_secret) on your server
|
v
Compare computed signature to X-Signature header value
|
+----+----+
| |
match no match
| |
v v
ACCEPT REJECT
process 401 Unauthorized
event (do not process)လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
အောက်က code က Node ရဲ့ built-in crypto module ကို သုံးပြီး production မှာ တကယ်လုပ်ဆောင်တဲ့အတိုင်း အတိအကျ လုပ်ဆောင်ထားပါတယ် — simulation မဟုတ်ဘူး။ computeSignature က HMAC-SHA256 ကို run ပြီး hex digest ကို ပြန်ပေးပါတယ်။
verifyWebhook က signature ကို သီးခြားပြန်တွက်ပြီး header နဲ့ နှိုင်းယှဉ်ပါတယ် — ရိုးရိုး equality check အစား crypto.timingSafeEqual ကို သုံးထားတာက timing attack ကို ရှောင်ရှားပေးပါတယ်။
Script က check ကို နှစ်ကြိမ် run ပါတယ် — genuine signature တစ်ခုနဲ့ tampered signature တစ်ခု။ ကိုယ်တိုင် run ကြည့်ပြီး genuine ကို accept၊ tampered ကို reject လုပ်တယ်ဆိုတာ သေချာအောင် စစ်ကြည့်ပါ။
Request တစ်ခုဟာ သင့် webhook URL ကို ရောက်ရှိတယ်ဆိုတာနဲ့ လုံးဝ မယုံပါနဲ့
Endpoint ကို ရောက်ရှိတယ်ဆိုတာက ဘယ်သူပို့သလဲဆိုတာ ဘာမှ သက်သေမပြနိုင်ပါဘူး။ Body ကို မဖတ်ခင် ဒါမှမဟုတ် အဲဒါအပေါ် အခြေခံ မလုပ်ဆောင်ခင် signature ကို အမြဲ verify လုပ်ပါ — အတည်မပြုရသေးတဲ့ request တိုင်းကို အန္တရာယ်ရှိသလို သဘောထားပါ။
အတူတူ စမ်းရေးကြည့်မယ်
const crypto = require("crypto");
const sharedSecret = "whsec_test_12345";
const payload = JSON.stringify({ type: "payment.completed", data: { orderId: "ord_482", amount: 4200 } });
function computeSignature(body, secret) {
return crypto.createHmac("sha256", secret).update(body).digest("hex");
}
function verifyWebhook(body, secret, signatureHeader) {
const expected = computeSignature(body, secret);
const isValid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
return isValid ? "ACCEPTED: signature matches" : "REJECTED: signature mismatch (401)";
}
const genuineSignature = computeSignature(payload, sharedSecret);
const tamperedSignature = genuineSignature.slice(0, -4) + "0000";
console.log("Payload:", payload);
console.log("Genuine signature:", genuineSignature);
console.log("Tampered signature:", tamperedSignature);
console.log("Check with genuine signature ->", verifyWebhook(payload, sharedSecret, genuineSignature));
console.log("Check with tampered signature ->", verifyWebhook(payload, sharedSecret, tamperedSignature));Payload: {"type":"payment.completed","data":{"orderId":"ord_482","amount":4200}}
Genuine signature: 40cc1dccb8401a94ef06c53b4a300a0112a6df9d8cf6f06962044edfadb37696
Tampered signature: 40cc1dccb8401a94ef06c53b4a300a0112a6df9d8cf6f06962044edfadb30000
Check with genuine signature -> ACCEPTED: signature matches
Check with tampered signature -> REJECTED: signature mismatch (401)၅ မိနစ် စမ်းကြည့်
Included timestamp ဟာ ၅ မိနစ်ထက် ပိုအိုနေရင် webhook ကို ငြင်းပယ်မယ့် checkTimestamp function တစ်ခု ထပ်ထည့်ပြီး verifyWebhook ထဲမှာ signature check မလုပ်ခင် ခေါ်သုံးပါ။
သတိလေးတစ်ချက်
Timing-safe comparison function မသုံးဘဲ ရိုးရိုး === ဒါမှမဟုတ် string equality check နဲ့ signature ကို နှိုင်းယှဉ်ခြင်း။
Provider signed လုပ်ခဲ့တဲ့ raw bytes အတိအကျအစား re-serialize ဒါမှမဟုတ် reformat လုပ်ထားတဲ့ body အပေါ် signature ကို verify လုပ်ခြင်း။
HMAC — Wikipedia — API Integration & Webhooks