ตรวจสอบ Webhook Signature ให้ถูกต้องด้วย Webhook Signature Verifier
เครื่องมือ Webhook Signature Verifier ช่วยตรวจสอบและสร้าง HMAC signature สำหรับ webhook ของ Stripe, GitHub และ Slack ได้ฟรีในเบราว์เซอร์ พร้อมเช็ค timestamp tolerance และ snippet โค้ดที่เอาไปใช้ได้ทันที
Table of Contents
Webhook เป็นหัวใจของระบบยุคนี้ ไม่ว่าจะเป็นการยืนยันการชำระเงินจาก Stripe การ trigger pipeline เมื่อ push code ขึ้น GitHub หรือการส่งข้อความเข้า Slack ทุกเหตุการณ์เหล่านี้ควรได้รับการพิสูจน์ก่อนว่ามาจากผู้ให้บริการจริง ไม่ใช่คำขอปลอมที่แอบอ้างชื่อเข้ามา แต่เมื่อการตรวจสอบ signature ล้มเหลว นักพัฒนามักเจอแค่ข้อความ 400 ที่ไม่บอกสาเหตุชัดเจน Webhook Signature Verifier ช่วยเปลี่ยนการเดาให้กลายเป็นคำตอบที่ชัดเจน
เครื่องมือนี้ฟรีและทำงานทั้งหมดในเบราว์เซอร์ เพียงวาง payload, signing secret และ signature header ที่ได้รับมา เลือก scheme ของ Stripe, GitHub หรือ Slack แล้วเครื่องมือจะคำนวณ HMAC-SHA256 ใหม่ด้วย Web Crypto API แล้วเทียบกับค่าที่ได้รับ พร้อมทั้งตรวจ timestamp tolerance เพื่อบอกว่า signature ไม่ได้แค่ถูกต้องแต่ยัง "สด" อยู่ด้วย และยังสร้าง snippet สำหรับตรวจสอบใน backend ของคุณให้ทันที
บทความนี้จะพาไปดูว่า signature verification ทำงานอย่างไร แต่ละผู้ให้บริการต่างกันตรงไหน วิธีดีบัก webhook ที่โดนปฏิเสธ และแนวปฏิบัติที่ช่วยกัน webhook ปลอมไม่ให้หลุดเข้าสู่ระบบ
ทำไมต้องใช้ Webhook Signature Verifier?
- ดีบัก webhook ที่โดนปฏิเสธ. นี่คืองานหลักของเครื่องมือนี้ แทนที่จะใส่ log ทีละจุด deploy ใหม่ แล้วไปกด trigger ใน dashboard ของผู้ให้บริการ ให้วาง payload, secret และ signature header ลงไป แล้วจะเห็นทันทีว่า signature ที่คำนวณได้ตรงกับที่ได้รับหรือไม่
- ทดสอบโดยไม่ต้อง deploy. logic การตรวจสอบมักอยู่ฝั่ง server แต่คุณยืนยันสมมติฐานได้ในเครื่องก่อนแตะโค้ด backend แม้แต่บรรทัดเดียว
- แก้ปัญหา scheme ที่ต่างกัน. Stripe เซ็น string ที่มี timestamp นำหน้า, GitHub เซ็น raw body ตรง ๆ และ Slack ใส่ version tag v0 เครื่องมือจะประกอบ signed string ให้ถูกแบบตามที่คุณเลือก
- เช็ค replay protection. signature ที่ถูกต้องทางคณิตศาสตร์แต่เก่ามากก็ยังเป็น replay attack ได้ การตรวจ timestamp tolerance จะบอกว่า signature ยังอยู่ในหน้าต่างที่ยอมรับหรือไม่
- ได้โค้ดพร้อมใช้. ทุกการตรวจสอบจะได้ snippet ตามรูปแบบที่ผู้ให้บริการระบุไว้ สิ่งที่พิสูจน์ได้ในเครื่องมือคือสิ่งที่จะเอาไปใช้จริง
- ข้อมูลลับอยู่กับคุณ. การ hash ทั้งหมดเกิดขึ้นในเบราว์เซอร์ payload และ signing secret ไม่เคยถูกส่งออกจากเครื่อง
ฟีเจอร์หลัก
| ฟีเจอร์ | ทำอะไร |
|---|---|
| ตรวจ HMAC signature | คำนวณ HMAC-SHA256 ใหม่ตาม scheme ของ Stripe, GitHub หรือ Slack แล้วเทียบกับ signature ที่ได้รับ |
| ตรวจ timestamp tolerance | อ่าน timestamp จาก signature header และรายงานว่าอยู่ใน replay window หรือไม่ |
| สร้าง signature | สร้าง signature ที่จัดรูปแบบถูกต้องจาก payload และ secret เหมาะกับการเซ็นคำขอทดสอบและ fixture |
| Snippet พร้อมใช้ | สร้างโค้ดตรวจสอบตามสูตรที่แต่ละผู้ให้บริการระบุไว้ |
| ทำงานในเบราว์เซอร์ | hashing และการเทียบทั้งหมดรันผ่าน Web Crypto API ในเครื่อง ไม่มีการอัปโหลด |
จุดที่ควรรู้เพิ่มเติม:
- เครื่องมือตรวจกับ raw payload bytes ซึ่งสำคัญมาก เพราะ webhook ที่โดนปฏิเสธส่วนใหญ่โดนเพราะ body ถูก parse แล้ว serialize ใหม่ก่อน hashing
- ผลลัพธ์แยกสามกรณีให้ชัดเจน: signature ไม่ตรง, timestamp เกิน tolerance หรือผ่านทั้งหมด ทำให้รู้ว่าต้องแก้ชั้นไหน
วิธีใช้งาน Webhook Signature Verifier
- วาง webhook payload. คัดลอก raw request body ตามที่ server ได้รับมาพอดี รวมถึง line ending และช่องว่าง ถ้ากำลังดีบักให้เอาจาก request log แทนการจัดรูป JSON ใหม่
- วาง secret และ signature header ที่ได้รับ. ใส่ signing secret จาก dashboard ของผู้ให้บริการ (ฝั่ง Stripe คือ key รูปแบบ whsec_...) และค่า header เต็ม เช่น Stripe-Signature, X-Hub-Signature-256 หรือ X-Slack-Signature
- เลือก signing scheme. เลือก Stripe, GitHub หรือ Slack เครื่องมือจะประกอบ signed string ที่ถูกต้อง ไม่ว่าจะเป็น timestamp + payload, payload อย่างเดียว หรือแบบมี v0 นำหน้า
- เทียบ computed กับ received. เครื่องมือคำนวณ digest ที่คาดหวังแล้วเทียบแบบ constant-time กับ signature ที่ได้รับ เหมือนที่โค้ด production ที่แข็งแรงควรทำ
- อ่านผล timestamp check. signature อาจถูกต้องแต่ไม่ปลอดภัยถ้า timestamp เก่าหลายชั่วโมง ผล tolerance จะจับกรณีนี้ให้เห็นชัด
ทำไม Signature จึงหยุด Webhook ปลอมได้
ทุก scheme เหล่านี้สร้างอยู่บน HMAC-SHA256 ซึ่งผสม secret key เข้ากับข้อความแล้วให้ digest ความยาวคงที่ คุณสมบัติที่ได้มีสองอย่าง หนึ่งคือ integrity ถ้า payload เปลี่ยนแม้แต่ไบต์เดียว digest จะเปลี่ยนไปทั้งหมด สองคือ authenticity มีเพียงผู้ถือ secret เท่านั้นที่สร้าง digest ที่ตรงกันได้ ผู้โจมตีจะ POST อะไรเข้า endpoint ก็ได้ แต่ปลอม signature ไม่ได้ถ้าไม่มี secret และ server ควรปฏิเสธคำขอก่อนประมวลผลข้อมูลใด ๆ
แต่ละผู้ให้บริการกำหนดว่าไบต์ส่วนไหนถูกเซ็น:
- Stripe. header Stripe-Signature มีหน้าตาแบบ t=1614556800,v1=5257a869... signed string คือ timestamp, จุด และ raw body ในรูป {t}.{payload} โดยใช้ signing secret ของ endpoint ถ้า header มีค่า v1 หลายค่าตอนหมุนเวียน secret ให้เทียบกับทุกค่า
- GitHub. header X-Hub-Signature-256 มีหน้าตาแบบ sha256=6c2f45... signed string คือ raw request body ตรง ๆ ใช้ webhook secret ที่ตั้งไว้ที่ repository หรือ app
- Slack. header X-Slack-Signature มีหน้าตาแบบ v0=ba88e4af... signed string คือ v0:{timestamp}:{raw body} ใช้ signing secret ของ app ไม่ใช่ bot token
timestamp tolerance คืออีกครึ่งหนึ่งของการป้องกัน HMAC พิสูจน์ได้ว่าคำขอถูกเซ็นสักครั้ง แต่ไม่ได้บอกว่าเมื่อไร ถ้าไม่มีกฎเรื่องความสด คำขอที่ถูกดักไว้จะใช้ได้ตลอดไป ผู้ให้บริการจึงใส่ timestamp ไว้ใน header และคาดหวังให้ปฏิเสธของที่เก่ากว่า tolerance window ซึ่งเอกสารของ Stripe แนะนำที่ห้านาที หน้าต่างนี้คือ replay protection ผู้โจมตีที่ดัก request ที่เซ็นถูกต้องมาได้จะเอาไปใช้ซ้ำวันหลังไม่ได้
การเทียบแบบ constant-time คือส่วนที่สาม การเทียบ string แบบ equality ปกติมักหยุดที่ไบต์แรกที่ต่างกัน และความต่างของเวลาที่วัดได้เคยถูกใช้ปลอม digest ทีละไบต์ constant-time comparison จะเดินครบทุกไบต์ไม่ว่าผลจะเป็นอย่างไร ซึ่งเป็นแบบที่เครื่องมือและ snippet ใช้
บั๊กคลาสสิกที่ควรจำไว้ การ parse payload สองรอบ คืออ่าน body เป็น JSON แล้วไป hash เวอร์ชันที่ serialize ใหม่ จะทำให้ตรวจไม่ผ่านเพราะลำดับ key และช่องว่างเปลี่ยน การใช้ credential ผิดตัวก็เช่นกัน เช่น ใช้ค่าอื่นแทน whsec_ ของ Stripe หรือใช้ bot token แทน signing secret ของ Slack การคัดลอก body แล้วมี newline เกินหรือช่องว่างหายไปก็เจอบ่อย และการตรวจหลัง body ผ่าน queue ที่ไม่คง raw bytes ไว้ก็เป็นสาเหตุที่พบได้เช่นกัน
กรณีใช้งานจริง
ดีบัก Stripe integration ที่ตรวจไม่ผ่าน
Flow ชำระเงินทำงานปกติ แต่ log ฝั่ง webhook โชว์ 400 ที่ event paymentintent.succeeded ให้คัดลอก raw body จาก log, secret whsec จาก dashboard และ header Stripe-Signature มาใส่ในเครื่องมือ ถ้า signature ไม่ตรงมักชี้ไปที่การ serialize body ใหม่ ถ้า timestamp ไม่ผ่านมักเป็นนาฬิกา server เพี้ยน ถ้าตรวจผ่านในเครื่องแต่ production ยังพัง ให้สงสัย proxy หรือ gateway ที่แก้ request body ระหว่างทาง
ทดสอบ replay ในเครื่อง
ใช้โหมดสร้าง signature เซ็น payload ทดสอบด้วย secret จริง แล้วยิงซ้ำเข้า endpoint ที่รันอยู่ในเครื่อง วิธีนี้เช็ค idempotency ของ handler ได้ และเพราะคุณคุม timestamp เอง คุณยังพิสูจน์ได้ว่า signature หมดอายุถูกปฏิเสธ ส่วนตัวที่สดผ่านอย่างถูกต้อง
ตรวจสอบความปลอดภัยของ webhook endpoint
กำลัง audit handler ของทีม? เช็คว่ามีการตรวจ signature หรือเปล่า บังคับ replay window หรือไม่ และเทียบ digest แบบ constant-time หรือเปล่า ลองแก้ payload ที่เก็บไว้หนึ่งตัวอักษรแล้วรันตรวจในเครื่องมือซ้ำ คุณจะได้หลักฐานชัดเจนว่าทำไมการตรวจ signature ต้องมาก่อน business logic เสมอ
สอนความปลอดภัยเรื่อง webhook
เครื่องมือนี้เป็น demo ในห้องเรียนที่กระชับ เซ็น payload หนึ่งชุด แก้ไบต์เดียว แล้วโชว์ว่า digest เปลี่ยนไปทั้งหมด จากนั้นตั้ง timestamp ให้เก่ามากแล้วดูว่า replay window ปฏิเสธ signature ที่ถูกต้องอย่างไร
แนวปฏิบัติที่ดีที่สุด
- ตรวจก่อนประมวลผลเสมอ. ถือว่า webhook ที่ยังไม่ตรวจเป็น input ที่ไม่น่าเชื่อถือ ไม่ว่า JSON จะดูน่าเชื่อแค่ไหน
- บังคับ replay window. ปฏิเสธ signature ที่ timestamp เกิน tolerance ไม่ใช่แค่ตัวที่ digest ไม่ตรง
- ใช้การเทียบ constant-time. อย่าเทียบ digest ด้วย equality ธรรมดา ให้ใช้ฟังก์ชัน constant-time หรือ helper ของ framework
- hash จาก raw bytes. ตรวจกับ body ตรงที่ server ได้รับ ก่อนการ parse หรือ serialize JSON ใด ๆ
- อย่า log secret. เก็บ signing secret ให้พ้น log, ไฟล์แนบ ticket และแชท ถือว่าเป็นรหัสผ่านระดับฐานข้อมูล
- เตรียมรับการหมุนเวียน secret. รองรับการตรวจหลาย secret ที่ยังใช้งานอยู่ เพื่อหมุนเวียนได้โดยไม่ทำ webhook ที่กำลังบินหล่นหาย
พร้อมเลิกเดาแล้วว่าทำไม webhook ถึงพัง? เปิด Webhook Signature Verifier วาง payload, secret และ signature header แล้วรับคำตอบที่ชัดเจนในไม่กี่วินาที พร้อม snippet ที่เอาไปวางใน handler ได้ทันที
เครื่องมือที่เกี่ยวข้องที่คุณอาจสนใจ:
- Hash Generator — สร้างและตรวจ digest อย่าง SHA-256 และ hash อื่น ๆ
- JWT Decoder — ถอดและดู header, payload และ signature ของ JSON Web Token
- Base64 Encoder — เข้ารหัสหรือถอดรหัส Base64 สำหรับ header และ fixture
ใช้งานอย่างปลอดภัยนะ!
คำถามที่พบบ่อย
ถ: ใช้เครื่องมือนี้แล้ว signing secret ปลอดภัยไหม? ตอบ: ปลอดภัยครับ การคำนวณ HMAC และการเทียบทั้งหมดเกิดขึ้นในเบราว์เซอร์ผ่าน Web Crypto API payload, secret และ signature ไม่เคยออกจากเครื่องของคุณ
ถ: ทำไม signature ตรงในเครื่องมือแต่ production ยังตรวจไม่ผ่าน? ตอบ: สาเหตุที่พบบ่อยที่สุดคือ body เปลี่ยนไปก่อนการ hash เช่น framework parse แล้ว serialize JSON ใหม่, proxy แก้ request หรือ middleware ตัดช่องว่าง ให้ตรวจกับ raw bytes ตรงที่ server ได้รับมา
ถ: ควรตั้ง timestamp tolerance เท่าไร? ตอบ: ห้านาทีคือค่าที่ Stripe แนะนำและเป็นค่าเริ่มต้นที่สมเหตุสมผลสำหรับระบบส่วนใหญ่ ถ้า handler เป็น idempotent และนาฬิกา server sync ด้วย NTP คุณบีบให้แคบลงได้
ถ: ใช้กับผู้ให้บริการอื่นนอกจาก Stripe, GitHub และ Slack ได้ไหม? ตอบ: มักได้ครับ ผู้ให้บริการใดที่เซ็นด้วย HMAC-SHA256 จะตรวจผ่านถ้ารูปแบบ signed string ตรงกับหนึ่งในสาม scheme นี้ คือแบบ payload อย่างเดียวแบบ GitHub หรือแบบมี timestamp นำหน้าแบบ Stripe และ Slack ให้ดูเอกสารของผู้ให้บริการว่าประกอบ string อย่างไร