ทำไมระบบขึ้น Signature Invalid ทั้งที่ Webhook เชื่อมต่อได้ปกติ

สรุปสั้น ๆ
Signature Invalid มักเกิดจากการคำนวณค่า HMAC-SHA256 ผิดพลาด เช่น ใช้ Channel Secret ผิดตัว หรือมีการแก้ไข Body ของคำขอ เช่น Middleware ที่ Parse JSON ก่อนนำไปคำนวณลายเซ็น ทำให้ค่าที่คำนวณได้ไม่ตรงกับที่ LINE ส่งมา
เมื่อระบบหลังบ้านตรวจสอบ Signature ของคำขอที่ส่งมาจาก LINE แล้วพบว่าค่าที่คำนวณได้ไม่ตรงกับค่าที่ LINE ส่งมาใน Header ระบบจะปฏิเสธคำขอนั้นและแสดงข้อความ Signature Invalid ทันที แม้ Webhook จะเชื่อมต่อได้ปกติและรับข้อมูลเข้ามาจริง ทำให้ทีมงานสับสนว่าทำไมระบบถึงไม่ยอมรับคำขอที่ดูเหมือนถูกต้อง
ความสับสนนี้ยิ่งชัดเจนขึ้นเมื่อทีมงานลองส่งคำขอทดสอบผ่านปุ่ม Verify ใน LINE Developers Console แล้วผ่านได้ปกติ แต่พอมีข้อความจริงจากลูกค้าเข้ามากลับถูกปฏิเสธ ทำให้เกิดคำถามว่าความแตกต่างระหว่างคำขอทดสอบกับคำขอจริงอยู่ตรงไหน และทำไมผลลัพธ์ถึงต่างกัน
ปัญหานี้พบบ่อยในทีมที่เพิ่งพัฒนาระบบรับ Webhook เอง หรือมีการปรับโครงสร้างโค้ดที่เกี่ยวข้องกับการรับคำขอ HTTP โดยไม่ได้ตรวจสอบผลกระทบต่อการคำนวณลายเซ็นอย่างละเอียด โดยเฉพาะทีมที่ใช้เฟรมเวิร์กสมัยใหม่ซึ่งมักมี Middleware ที่ทำงานอัตโนมัติหลายชั้นก่อนที่โค้ดของทีมงานจะได้เข้าถึงข้อมูลจริง ๆ
บทความนี้ไล่สาเหตุที่พบบ่อยที่สุดของปัญหา Signature Invalid และแนวทางแก้ไขทีละขั้นตอน พร้อมตัวอย่างสถานการณ์ที่เกิดขึ้นจริงในทีมที่ดูแลระบบ LINE OA ให้ลูกค้าหลายราย
หลักการทำงานของการตรวจสอบ Signature
เมื่อ LINE ส่งคำขอ Webhook มายังเซิร์ฟเวอร์ปลายทาง จะแนบค่า Signature ไว้ใน Header ของคำขอ ซึ่งคำนวณจากเนื้อหา Body ทั้งหมดของคำขอด้วยอัลกอริทึม HMAC-SHA256 โดยใช้ Channel Secret ของบัญชีนั้นเป็นกุญแจในการคำนวณ กระบวนการนี้คือแก่นของการตรวจสอบลายเซ็น Webhookที่ทุกระบบต้องทำให้ถูกต้อง
ฝั่งเซิร์ฟเวอร์ปลายทางต้องนำ Body ของคำขอที่ได้รับมาคำนวณ HMAC-SHA256 ด้วย Channel Secret เดียวกัน แล้วเปรียบเทียบผลลัพธ์กับค่า Signature ที่ LINE ส่งมา หากค่าทั้งสองตรงกันแสดงว่าคำขอนั้นมาจาก LINE จริงและไม่ถูกแก้ไขระหว่างทาง
กลไกนี้ออกแบบมาเพื่อป้องกันการปลอมแปลงคำขอ เพราะแม้ผู้ไม่หวังดีจะรู้ URL ปลายทางของ Webhook แต่หากไม่มี Channel Secret ที่ถูกต้อง ก็ไม่สามารถคำนวณค่า Signature ที่ตรงกับที่ระบบคาดหวังได้ จึงเป็นชั้นความปลอดภัยสำคัญที่ไม่ควรถูกปิดหรือข้ามไปแม้ในช่วงพัฒนาระบบ
สาเหตุที่พบบ่อยของ Signature Invalid
- ใช้ Channel Secret ผิดตัว เช่น มีหลายบัญชี LINE OA แล้วนำ Channel Secret ของบัญชีอื่นมาคำนวณ
- Middleware ของเฟรมเวิร์กที่ใช้ Parse Body เป็น JSON ก่อนที่จะนำไปคำนวณลายเซ็น ทำให้รูปแบบของ Body ที่นำไปคำนวณเปลี่ยนไปจาก Raw Body ต้นฉบับ
- มีการ Encode หรือ Decode ตัวอักษรบางตัวใน Body ระหว่างทาง เช่น ผ่าน Proxy หรือ Load Balancer ที่ปรับแต่ง Header หรือ Body โดยไม่ตั้งใจ
- Channel Secret มีการสร้างใหม่ในคอนโซล LINE Developers แต่ระบบหลังบ้านยังใช้ค่าเก่าที่ถูกยกเลิกไปแล้ว
ปัญหาเรื่อง Raw Body ที่พบบ่อยที่สุด
ปัญหาที่พบบ่อยที่สุดในทีมที่ใช้เฟรมเวิร์กสมัยใหม่คือ Middleware ที่ทำหน้าที่ Parse Body ของคำขอให้เป็น JSON Object โดยอัตโนมัติก่อนที่โค้ดของเราจะได้เข้าถึงข้อมูล ทำให้เมื่อถึงขั้นตอนคำนวณ Signature เราไม่มี Raw Body ต้นฉบับให้ใช้คำนวณอีกต่อไป เพราะถูกแปลงเป็น Object ไปแล้ว
การแก้ไขที่ถูกต้องคือต้องตั้งค่า Middleware ให้เก็บ Raw Body ไว้ก่อนที่จะ Parse เป็น JSON เช่น เก็บไว้ในตัวแปรแยกต่างหาก แล้วนำ Raw Body นั้นไปคำนวณ Signature แทนที่จะใช้ Object ที่ถูกแปลงแล้ว เพราะแม้ Object ที่แปลงกลับมาเป็น String อีกครั้งจะดูเหมือนเดิม แต่รูปแบบของช่องว่าง การเรียงลำดับ Key หรืออักขระพิเศษอาจต่างจาก Raw Body ต้นฉบับเพียงเล็กน้อย ซึ่งเพียงพอที่จะทำให้ค่า Hash ที่คำนวณได้ต่างกันโดยสิ้นเชิง
หลายเฟรมเวิร์กมีวิธีเฉพาะในการเข้าถึง Raw Body เช่น การตั้งค่า Option พิเศษตอนสร้าง Middleware หรือใช้ไลบรารีเสริมที่ออกแบบมาสำหรับเก็บ Body ต้นฉบับไว้คู่ขนานกับ Body ที่ถูก Parse แล้ว ทีมงานควรศึกษาวิธีที่ถูกต้องของเฟรมเวิร์กที่ใช้งานอยู่โดยเฉพาะ แทนที่จะคัดลอกโค้ดตัวอย่างจากเฟรมเวิร์กอื่นมาใช้ตรง ๆ เพราะแต่ละเฟรมเวิร์กมีกลไกจัดการ Body ที่แตกต่างกัน
วิธีวินิจฉัยปัญหาทีละขั้น
- ตรวจสอบว่า Channel Secret ที่ใช้ในระบบหลังบ้านตรงกับค่าล่าสุดใน LINE Developers Console ของบัญชีที่ถูกต้อง
- ตรวจสอบโค้ดว่ามีการเก็บ Raw Body ไว้ก่อนที่ Middleware จะ Parse เป็น JSON หรือไม่
- เขียนสคริปต์ทดสอบแยกเพื่อคำนวณ Signature จาก Body ตัวอย่างและ Channel Secret ที่ทราบค่าแน่นอน แล้วเทียบกับผลลัพธ์ที่ควรจะได้ตามเอกสารของ LINE ซึ่งใช้แนวคิดคล้ายกับการออกแบบ Idempotency Keyที่ต้องคำนวณซ้ำแล้วได้ผลลัพธ์เดิมทุกครั้ง
- ตรวจสอบว่ามี Proxy หรือ Load Balancer อยู่ระหว่างทางที่อาจปรับแต่งเนื้อหาของคำขอก่อนถึงเซิร์ฟเวอร์ปลายทางหรือไม่
ตัวอย่างสมมติ: ไล่หาสาเหตุ Signature Invalid
| จุดตรวจสอบ | สิ่งที่พบ (ตัวอย่างสมมติ) | ผลกระทบ |
|---|---|---|
| Middleware | ใช้ body-parser แปลง Body เป็น JSON ก่อนคำนวณ Signature | Raw Body หายไป คำนวณลายเซ็นผิดค่าทุกครั้ง |
| Channel Secret | ใช้ค่าของบัญชี LINE OA อื่นที่มีหลายบัญชีในระบบเดียวกัน | ลายเซ็นที่คำนวณได้ไม่ตรงกับที่ LINE คาดหวังเลย |
| Load Balancer | ปรับแต่ง Header บางตัวโดยอัตโนมัติระหว่างทาง | Body หรือ Header เปลี่ยนแปลงเล็กน้อยจนคำนวณลายเซ็นผิด |
มุมมองจากทีมที่เจอปัญหานี้ตอน Deploy ระบบใหม่
ทีมพัฒนาที่ย้ายระบบรับ Webhook จากเฟรมเวิร์กเก่าไปยังเฟรมเวิร์กใหม่ และเคยศึกษาโครงสร้าง Event Schema ของ LINE Webhookมาก่อน มักเจอปัญหา Signature Invalid ทันทีหลัง Deploy เพราะเฟรมเวิร์กใหม่มี Middleware เริ่มต้นที่ Parse Body อัตโนมัติแตกต่างจากระบบเดิม ทำให้โค้ดที่เคยทำงานถูกต้องในระบบเก่ากลับใช้ไม่ได้ในระบบใหม่ทันที
บทเรียนสำคัญคือทุกครั้งที่มีการเปลี่ยนเฟรมเวิร์กหรือ Middleware ที่เกี่ยวข้องกับการรับคำขอ HTTP ควรทดสอบการคำนวณ Signature ใหม่ทั้งหมดก่อน Deploy ขึ้นระบบจริง ไม่ควรสมมติว่าโค้ดส่วนนี้จะทำงานเหมือนเดิมโดยอัตโนมัติ
ข้อควรระวังเมื่อดูแลหลายบัญชี LINE OA พร้อมกัน
ทีมที่ดูแลระบบให้ลูกค้าหลายรายพร้อมกันมักเก็บ Channel Secret ของแต่ละบัญชีไว้ในระบบเดียวกัน ความเสี่ยงที่พบบ่อยคือการเขียนโค้ดที่ดึง Channel Secret ผิดตัวเมื่อมีคำขอเข้ามาจากบัญชีที่ไม่ตรงกับที่คาดไว้ โดยเฉพาะเมื่อระบบมีการ Routing คำขอ Webhook จากหลายบัญชีเข้ามาที่ Endpoint เดียวกัน
แนวทางที่ปลอดภัยกว่าคือแยก Endpoint สำหรับแต่ละบัญชีอย่างชัดเจน หรือถ้าจำเป็นต้องใช้ Endpoint เดียวกัน ควรมีกลไกระบุว่าคำขอนั้นมาจากบัญชีไหนอย่างแน่ชัดก่อนดึง Channel Secret ที่ถูกต้องมาใช้คำนวณ Signature หลักการนี้เกี่ยวโยงกับการออกแบบการติดตาม Postback Event ของ LINE Webhookที่ต้องแยกแยะที่มาของคำขอให้ชัดเจนเช่นกัน
อีกแนวทางหนึ่งที่ช่วยลดความเสี่ยงคือเก็บ Channel Secret ทุกบัญชีไว้ในรูปแบบ Key-Value ที่ Map กับ Channel ID อย่างชัดเจน แล้วให้ระบบดึง Channel Secret ที่ถูกต้องโดยอ้างอิงจาก Channel ID ที่แนบมาในคำขอ แทนที่จะเดาหรือใช้ค่าคงที่ตัวเดียวสำหรับทุกบัญชี ซึ่งเป็นวิธีที่ยืดหยุ่นและปลอดภัยกว่าเมื่อจำนวนบัญชีที่ดูแลเพิ่มขึ้นเรื่อย ๆ
วิธีทดสอบการคำนวณ Signature อย่างเป็นระบบ
แทนที่จะรอให้เกิดปัญหาจริงแล้วค่อยไล่หาสาเหตุ ทีมพัฒนาควรเขียนชุดทดสอบอัตโนมัติที่ตรวจสอบการคำนวณ Signature โดยเฉพาะ โดยใช้ Body ตัวอย่างที่ทราบค่าแน่นอนและ Channel Secret ทดสอบ แล้วเปรียบเทียบผลลัพธ์กับค่าที่คำนวณได้ตามเอกสารของ LINE เพื่อยืนยันว่าโค้ดคำนวณถูกต้องก่อน Deploy ทุกครั้ง
ชุดทดสอบนี้ควรถูกรันอัตโนมัติทุกครั้งที่มีการแก้ไขโค้ดที่เกี่ยวข้องกับการรับคำขอ HTTP หรือเปลี่ยนเฟรมเวิร์ก เพื่อจับปัญหาตั้งแต่ขั้นตอนพัฒนา ก่อนที่จะส่งผลกระทบต่อลูกค้าจริงในระบบ Production ซึ่งช่วยลดความเสี่ยงที่ปัญหาจะถูกค้นพบช้าหลังจากลูกค้าร้องเรียนเข้ามาแล้วเท่านั้น
การบันทึกเอกสารเพื่อป้องกันปัญหาซ้ำ
- บันทึกไว้อย่างชัดเจนว่าระบบเก็บ Raw Body ไว้ตรงจุดไหนของโค้ด และห้ามแก้ไขจุดนั้นโดยไม่ทดสอบ Signature ใหม่
- จดบันทึก Channel Secret ของแต่ละบัญชีไว้ในที่ปลอดภัยพร้อมระบุว่าใช้กับบัญชีไหน เพื่อลดความเสี่ยงเรื่องใช้ผิดตัวเมื่อดูแลหลายบัญชี
- เก็บประวัติการเปลี่ยนแปลง Middleware หรือเฟรมเวิร์กที่เกี่ยวข้องกับการรับคำขอ Webhook ไว้ เพื่อให้ไล่ย้อนกลับได้ง่ายเมื่อเกิดปัญหาหลัง Deploy ครั้งใดครั้งหนึ่ง
ทำแบบนี้แล้วพัง เพราะ… บทเรียนจากทีมที่เจอปัญหาซ้ำ
- ใช้ Body ที่ผ่านการ Parse เป็น JSON แล้วไปคำนวณ Signature — พังเพราะรูปแบบของ Body ต่างจาก Raw Body ต้นฉบับเพียงเล็กน้อยก็ทำให้ Hash ผิดค่าทันที
- ใช้ Channel Secret ของบัญชีอื่นในระบบที่ดูแลหลายบัญชี — พังเพราะลายเซ็นที่คำนวณได้ไม่มีทางตรงกับที่ LINE คาดหวังเลย
- ไม่ทดสอบการคำนวณ Signature ใหม่หลังเปลี่ยนเฟรมเวิร์กหรือ Middleware — พังเพราะโค้ดที่เคยทำงานถูกต้องอาจใช้ไม่ได้ทันทีในสภาพแวดล้อมใหม่
- ไม่มีเอกสารบันทึกจุดที่เก็บ Raw Body ไว้ในโค้ด — พังเพราะเมื่อทีมงานคนใหม่มาแก้โค้ดโดยไม่รู้ความสำคัญของจุดนี้ อาจลบหรือย้ายโดยไม่ตั้งใจจนระบบตรวจสอบ Signature ล้มเหลวทั้งหมด
สรุป
Signature Invalid เกิดจากการที่ค่า HMAC-SHA256 ที่เซิร์ฟเวอร์ปลายทางคำนวณได้ไม่ตรงกับค่าที่ LINE ส่งมา สาเหตุหลักคือการใช้ Body ที่ผ่านการ Parse แล้วแทน Raw Body หรือใช้ Channel Secret ผิดตัว
การแก้ไขต้องเริ่มจากตรวจสอบว่าระบบเก็บ Raw Body ไว้ก่อน Parse เป็น JSON หรือไม่ และยืนยันว่า Channel Secret ที่ใช้ตรงกับบัญชีที่ถูกต้อง โดยเฉพาะในระบบที่ดูแลหลายบัญชีพร้อมกัน
ที่สำคัญคือไม่ควรปิดการตรวจสอบ Signature เพื่อแก้ปัญหาเฉพาะหน้า เพราะเป็นชั้นความปลอดภัยสำคัญที่ป้องกันคำขอปลอมเข้าสู่ระบบ ควรแก้ที่ต้นเหตุของการคำนวณให้ถูกต้องแทน และเขียนชุดทดสอบอัตโนมัติไว้ป้องกันปัญหาซ้ำในอนาคต
- Signature Invalid คือคำขอมาถึงแล้วแต่ถูกปฏิเสธเพราะลายเซ็นไม่ตรง ต่างจาก Webhook ไม่เข้าที่คำขอไปไม่ถึงเลย
- สาเหตุหลักคือ Middleware Parse Body ก่อนคำนวณ Signature และการใช้ Channel Secret ผิดตัว
- ควรทดสอบการคำนวณ Signature ใหม่ทุกครั้งที่เปลี่ยนเฟรมเวิร์กหรือ Middleware ที่เกี่ยวข้องกับการรับคำขอ
คำถามที่พบบ่อย
Signature Invalid ต่างจาก Webhook ไม่เข้าอย่างไร
Signature Invalid หมายถึงคำขอเข้ามาถึงเซิร์ฟเวอร์แล้วแต่ถูกปฏิเสธเพราะตรวจสอบลายเซ็นไม่ผ่าน ส่วน Webhook ไม่เข้าหมายถึงคำขอไม่ถึงเซิร์ฟเวอร์เลยตั้งแต่ต้น ทั้งสองมีสาเหตุและวิธีแก้ที่แตกต่างกัน
ทำไมทดสอบด้วยปุ่ม Verify แล้วผ่าน แต่คำขอจริงกลับ Signature Invalid
ปุ่ม Verify อาจส่ง Body ทดสอบที่ง่ายกว่าคำขอจริง หรือบางกรณีข้ามการตรวจสอบลายเซ็นบางส่วน ควรทดสอบด้วยคำขอจริงจากผู้ใช้เพิ่มเติมเพื่อยืนยันว่าการตรวจสอบทำงานถูกต้องในทุกสถานการณ์
ต้องเก็บ Raw Body ไว้นานแค่ไหน
ไม่จำเป็นต้องเก็บถาวร เพียงแค่เก็บไว้ชั่วคราวในหน่วยความจำระหว่างขั้นตอนการตรวจสอบ Signature เท่านั้น หลังจากตรวจสอบผ่านแล้วสามารถใช้ข้อมูลที่ Parse เป็น JSON ต่อได้ตามปกติ
ถ้ามีหลายบัญชี LINE OA ควรตั้ง Endpoint แยกกันไหม
แนะนำให้แยก Endpoint ต่อบัญชีถ้าเป็นไปได้ เพราะช่วยลดความเสี่ยงเรื่องการดึง Channel Secret ผิดตัว และยังช่วยให้การดีบักปัญหาทำได้ง่ายขึ้นเมื่อมีปัญหาเกิดกับบัญชีใดบัญชีหนึ่งโดยเฉพาะ
การแก้ปัญหานี้ต้องแก้ที่ฝั่ง LINE หรือฝั่งเซิร์ฟเวอร์ของเรา
ต้องแก้ที่ฝั่งเซิร์ฟเวอร์ของเราเป็นหลัก เพราะ Signature ที่ LINE ส่งมาถูกต้องอยู่แล้วตามค่า Channel Secret ที่ตั้งไว้ ปัญหาส่วนใหญ่เกิดจากการคำนวณฝั่งเราเองที่ไม่ตรงตามมาตรฐานที่กำหนด
ควรปิดการตรวจสอบ Signature ชั่วคราวเพื่อแก้ปัญหาเฉพาะหน้าได้ไหม
ไม่ควรทำเด็ดขาด แม้จะช่วยให้ Webhook รับข้อมูลได้ทันทีในระยะสั้น แต่จะเปิดช่องให้คำขอปลอมที่ไม่ได้มาจาก LINE จริงเข้าสู่ระบบได้ ควรแก้ที่ต้นเหตุของการคำนวณ Signature แทนการปิดการตรวจสอบไปเลย
ลองตรวจด้วยตัวเอง
LINE Webhook Signature Validator
ทดสอบ signature กับ raw body และ channel secret ของคุณได้ในเบราว์เซอร์ — secret ไม่ถูกส่งขึ้นเซิร์ฟเวอร์
ตรวจ signature ฟรี →ใช้ฟรี ไม่ต้องสมัคร ไม่เก็บข้อมูลที่คุณกรอก
อ่านต่อแบบเจาะลึก
รู้ว่าโฆษณาเข้า LINE ทำยอดขายจริงแค่ไหน
linli ติดตามลูกค้าตั้งแต่คลิกโฆษณา กดเพิ่มเพื่อน ทักแชท จนปิดการขาย พร้อมส่ง Conversion กลับแพลตฟอร์มโฆษณา เริ่มฟรี 14 วัน
ทดลองใช้ฟรี 14 วันบทความที่เกี่ยวข้อง

ยิง TikTok Ads วันละ 5,000 บาท แต่ไม่รู้ว่ากี่ออเดอร์จริงมาจากแอดนั้น

นับ Repeat Purchase จาก Order ที่สองกับจาก LINE User ID เดิม ไม่ใช่วิธีเดียวกัน
