Verify Line Events Webhook บน Express.js จะได้ประมาณนี้ [TH]

ถ้าไม่อยากเหนื่อย, จบปัญหาเรื่อง Emoji และยังเอาแนวทางนี้ไปใช้กับ Facebook ได้อีกด้วย มาดูกันครับ

Siwawes Wongcharoen
3 min readAug 15, 2021

ขอไม่เล่าว่าทำไมเราจึงต้องทำ Line Signature Validation นะครับ เพราะเชื่อว่า ใครหลงเข้ามาตรงนี้ ก็น่าจะรู้อยู่แล้วว่าทำไมจึงต้องทำ

ถ้าอยากจะรู้เรื่องราวให้กระจ่าง เราก็พร้อมที่จะแถลงไข

ในแง่ของ Developer เรามักจะคิดกันว่า Endpoint ที่เราเตรียมไว้รับ Webhook จาก Line นั่น ไม่น่าจะมีคนอื่นรู้ จึงอาจจะไม่ระวัง ไม่ทันได้เตรียมการป้องกันการรับข้อมูลอันไม่พึงประสงค์

ในแง่ของ Security เค้าจะถามแน่ ๆ ว่า คุณจะรู้ได้อย่างไร ว่า ข้อมูลที่คุณรับมา มาจาก Line จริง ๆ

เอาละ ไม่ต้องกังวลไป เพราะทาง Line ก็ได้เตรียมวิธีการยืนยันไว้ให้เราแล้วครับ นั่นก็คือการทำ Signature Validation (หรือก็คือการเอาข้อมูลมา Hash ด้วย Key แล้วใช้ตัว Hash นี้เป็นตัวยืนยัน)

เมื่อพูดถึงเรื่องการความปลอดภัยข้อมูล นอกจากการทำ Signature Validation แล้ว ก็ยังมีวิธีอื่น ๆ ที่หลาย ๆ คนอาจจะรู้จักกันดี เช่น

  • Site-to-Site VPN
  • IP Whitelist
  • และอีกหลาย ๆ วิธี

แต่พอลองนึกภาพจากทางฝั่งของ Line ด้วย Site-to-Site VPN นี่ตัดไปได้เลย เพราะทำไม่ไหวแน่นอน ส่วน IP Whitelist เอาจริง ๆ มันปลอมได้ ฉะนั้นมันก็เหลือแนวทางเดียว คือการ Validate ตัวข้อมูลที่รับส่งกัน ด้วย Key ที่ถืออยู่ด้วยกันทั้งคนรับ/ส่งข้อมูลนั่นเองครับ ซึ่งในกรณีนี้ก็คือ Line Signature Validation นั่นเองครับ

สรุปแล้ว Signature Validation คืออะไร

โดยทั่วไปแล้ว หลักการของ Signature Validation คือ เอาข้อมูลที่ต้องการการตรวจสอบ มาทำ Hash ด้วย Key / Secret เพื่อให้ได้ค่าที่สามารถนำไปเปรียบเทียบได้ โดยจะทราบได้หากตัวข้อมูลมีการเปลี่ยนแปลงไประหว่างทางหรือไม่ นอกจากนี้ยังสามารถใช้ Hash นี้ เป็นตัวยืนยันได้ด้วยว่าข้อมูลชุดนี้มาจากต้นทางที่ถูกต้องจริง ๆ

เข้าเนื้อหาเลย ไม่อ้อมโลก

สิ่งที่เราต้องทำจริง ๆ คือเอา Request body มา Hash ด้วย HMAC-SHA256 เพื่อเทียบกับ x-line-signature ที่มากับ Request Header ว่าตรงกันหรือไม่

แต่ถ้า เราเอา Request body ที่ผ่าน JSON parser มาแล้ว มาพยายามแปลงกลับเป็น String เราอาจจะเจอปัญหา เช่น UTF-8 encoding ที่มาจากพวก Emoji ทั้งหลาย

ดังนั้นสิ่งที่เราควรทำคือ เอา Request body แบบ UTF-8 จริง ๆ ก่อนที่จะถูกแปลงโดย JSON parser มาใช้เลย

Note: ตัวอย่างนี้จะใช้ Express.js มีทั้ง JS และ TS นะครับ

ใช้ Express.js Middleware ธรรมดา ๆ นี่แหละ [Version สั้น แบบใช้ JS]

ขั้นแรกเราก็ใส่ Middleware ให้กับ Express.js ก่อนครับ

const express = require('express');
const app = express();
app.use(function (req, res, next) {
req.rawBody = '';
req.on('data', function (chunk) {
req.rawBody += chunk;
});
next();
});

Middleware ตัวนี้จะเพิ่ม req.rawBody ที่เป็น String UTF-8 ตรงไปตรงมาครับ

จากนั้น ก็จะเป็น Middleware สำหรับ Verify signature จริง ๆ แล้ว

const crypto = require('crypto');function lineSignatureValidationMiddleWare (req, res, next) {
const signature = crypto
.createHmac('SHA256', 'LineChannelSecret')
.update(req.rawBody)
.digest('base64');
// Compare x-line-signature with the signature
const headerXLineSignature = req.headers['x-line-signature'];
if (headerXLineSignature === signature) {
next();
} else {
res.sendStatus(401);
}
}

จากนี้ ก็สามารถเอา Middleware lineSignatureValidationMiddleWare ไปใช้กับ Route ที่รับ Webhook จาก Line ได้เลยครับ

Note: วิธีนี้ใช้กับ Facebook Webhook ได้ด้วยนะครับ

ใช้ Express.js Middleware ธรรมดา ๆ นี่แหละ [Version เต็ม แบบใช้ TS]

อธิบาย Code

บรรทัดที่ 8-14 อันนี้เป็นเรื่องของ Type Script ครับ เป็นอีกหนึ่งในวิธีที่ใช้เพิ่ม Member ให้กับ Interface ตรงนี้ผมจะเพิ่ม Member ที่ชื่อว่า rawBody กำหนด Type เป็น string เพื่อเอาไว้ใช้เก็บ body payload ในสภาพดั่งเดิมครับบรรทัดที่ 16-22 สร้าง Middleware ให้กับ Express.js ทำหน้าที่ เก็บ body payload ในสภาพดั่งเดิม ใส่ใน Request.rawBody ที่เตรียมไว้ในขั้นตอนก่อนหน้าครับบรรทัดที่ 28-43 สร้าง Middleware ที่ทำหน้าที่เปรียบเทียบ Signature ของ body payload ที่ได้มาจาก Request ที่ได้รับมาครับ

ผลที่ได้

เราจะโฟกัสกันที่การเปรียบเทียบ Signature ที่ได้มาจาก Request Header กับที่ได้จากการคำนวนเองนะครับ โดยสังเกตุจาก บรรทัดที่ขึ้นต้นด้วย i MDW signature: ครับ

ส่งข้อความด้วยภาษาไทยธรรมดา ๆ
ส่งข้อความด้วยภาษาไทย ภาษาอังกฤษ และ Emoji
ส่งข้อความด้วยญี่ปุ่น และ Emoji

สรุป

การทำ Signature Validation ด้วยวิธีนี้ ครอบคลุมทั้งภาษา และ Emoji แน่นอน

ในด้าน Security เราจำเป็นต้องตรวจสอบสิ่งที่เราได้รับมา ว่ามาจากที่ ๆ ควรจะมา และไม่ถูกเปลี่ยนแปลงระหว่างทางครับ

สรุปในแบบ Dev2Dev

เรื่อง Security เป็นหนึ่งในเรื่องที่เรามักจะพลาดกัน เพราะเราไม่รู้จริง ๆ แต่ตอนนี้รู้แล้ว ฉะนั้นก็ไม่น่าจะพลาดแล้วนะครับ 👌

Special Thanks to Jirawatee

--

--

No responses yet