DEV Community

Thanabodee Charoenpiriyakij
Thanabodee Charoenpiriyakij

Posted on

Validate signature สำหรับ LINE webhook

LINE webhook api มีสิ่งขั้นตอนนึงที่ต้องทำคือการ validate signature เพื่อยืนยันว่า request ที่เข้ามานั้นมาจาก Official Account ของเราจริงๆ ซึ่งถ้าใช้ภาษาที่ official LINE support แล้วละก็จะมีจะ function ให้ใช้งานอย่าง line-bot-sdk-go ที่มี ParseRequest(*http.Request) ให้ใช้งานก็จะ validate signature จาก request header ให้อัตโนมัติ

ถ้าลองลอกการทำงานของ validate signature เพื่อนำมาทำ webhook ใน​​ Elixir น่าจะได้ประมาณนี้

ขั้นตอนแรกสร้าง hash ด้วย HMAC-SHA256 โดยใช้ key เป็น ​channel secret และ data เป็น request body ที่ LINE ส่งเข้ามา function จะได้หน้าตาประมาณนี้

defp sign_signature(channel_secret, body) do
  :crypto.hmac(:sha256, channel_secret, body)
end

จากนั้น encode ด้วย base64 จะได้ signature ที่ LINE ส่งมาทาง request header ซึ่งใช้ pipe operator pipe ตัว hash ที่เราสร้างเข้าไป Base.encode เลยจะง่ายกว่า

defp sign_signature(channel_secret, body) do
  :crypto.hmac(:sha256, channel_secret, body) |> Base.encode64()
end

จากนั้นเอา signature ที่ได้ไป compare กับ signature จาก request header

def validate(signature, body, channel_secret) do
  validate(signature, sign_signature(channel_secret, body))
end

defp validate(signature, signature), do: :ok
defp validate(_, _), do: :invalid_signature

ผมสร้าง validate/3 เพื่อรับ signature จาก request header แล้วส่งเข้าไปที่ validate/2 เพื่อใช้ pattern matching ดูว่าเป็น signature เดียวกันก็จะตอบ :ok ไปเลยส่วน case อื่นก็ถือว่า :invalid_signature ไป

ปล. จริงๆ จะใช้ case <> do ... end ก็ได้ไม่ว่ากัน เพราะความตั้งใจผมคือต้องการแยกเคสให้อ่านง่าย

ผมลองสร้างตัวอย่างโดย port มาจาก echo_bot ที่อยู่ใน line-bot-sdk-go ที่นี่ลองดูหรือ discuss กันได้ครับ :)

ข้อควรระวัง

LINE จะ sign signature โดยรวม space ใน request body กรณีนี้จะเจอตอนที่ LINE ส่ง body แบบ indent JSON มาให้​ซึ่งหากเราใช้ Plug.Parsers แล้วมาดูจาก conn.body_params อาจจะผิดได้ (เคสนี้ผมเจอตอน verify webhook หาอยู่พักนึงถึงเข้าใจ)

Top comments (0)