[{"data":1,"prerenderedAt":297},["ShallowReactive",2],{"h1474062933":3,"h3319671882":195,"h1610204309":291},{"id":4,"title":5,"body":6,"description":28,"extension":185,"meta":186,"navigation":190,"path":191,"seo":192,"stem":193,"__hash__":194},"docs\u002Fja\u002Ftips\u002F2026\u002F06\u002F18\u002Fverify-webhook-signature.md","Messaging APIのWebhookの署名を検証しよう",{"type":7,"value":8,"toc":166},"minimark",[9],[10,11,12,20,25,29,38,41,44,49,52,55,59,62,75,78,82,90,98,118,121,128,132,143,150,154,157,160],"Tips",{},[13,14,17],"h1",{"id":15,"class":16},"","!mb-4",[18,19],"page-title",{},[21,22],"display-date",{"date":23,"class":24},"2026\u002F06\u002F18","!mb-20",[26,27,28],"p",{},"こんにちは。Messaging APIの開発を担当している奥薗（@mokuzon）です。",[26,30,31,32,37],{},"Messaging APIでボットを作るとき、ユーザーからのメッセージや友だち追加といったイベントは、",[33,34,36],"a",{"href":35},"\u002Freference\u002Fmessaging-api\u002F#webhooks","Webhook","としてみなさんのボットサーバーに届きます。このWebhookを受け取って処理するのがボット開発の出発点なわけですが、ここで一度立ち止まって考えておきたいことがあります。「そのリクエスト、本当にLINEプラットフォームから来たものですか？」という点です。",[26,39,40],{},"この記事では、Webhookのなりすまし対策として署名を検証しようという話を、簡単に紹介します。",[42,43],"toc",{},[45,46,48],"h2",{"id":47},"webhook-spoofing","Webhookのなりすましという問題",[26,50,51],{},"WebhookはHTTPSのPOSTリクエストとして届きます。裏を返すと、ボットサーバーのエンドポイントのURLさえ分かってしまえば、LINEプラットフォーム以外の第三者でも同じ形のリクエストを送れてしまう、ということでもあります。",[26,53,54],{},"もし署名を検証せず、届いたリクエストの中身をそのまま信じて処理してしまうと、攻撃者がでっち上げた偽のイベントを、本物のユーザーからのイベントとして処理してしまうおそれがあります。だからこそ、「届いたWebhookが本当にLINEプラットフォームから送られたものか」を確認する仕組みが必要になります。",[45,56,58],{"id":57},"ip-allowlist","送信元IPアドレスで絞る方法とその限界",[26,60,61],{},"「送信元が正しいか確認したい」と言われて、まず思いつくのは送信元IPアドレスでアクセスを制限する方法かもしれません。LINEプラットフォームのIPアドレス以外からのアクセスをブロックすれば、たしかになりすましは防げそうに見えます。",[26,63,64,65,69,70,74],{},"しかし、実際にはこの方法は採用できません。",[33,66,68],{"href":67},"\u002Fdocs\u002Fmessaging-api\u002Fdevelopment-guidelines\u002F#prohibiting-ip-address-restrictions","Messaging API開発ガイドライン","にも書かれているとおり、",[71,72,73],"strong",{},"LINEプラットフォームのIPアドレスは開示しておらず、また予告なく変更される場合がある","ためです。許可リストに登録すべきIPアドレスがそもそもわからないため、IPアドレスによるアクセス制限は実現できません。",[26,76,77],{},"ではどうするか。ここで登場するのが署名です。",[45,79,81],{"id":80},"signature-verification","署名を検証するという方法",[26,83,84,85,89],{},"LINEプラットフォームは、Webhookを送信するときにリクエストヘッダー",[86,87,88],"code",{},"x-line-signature","に「署名」を付けています。この署名を検証することで、IPアドレスに頼らずに送信元の正当性を確認できます。",[26,91,92,93,97],{},"仕組みはとてもシンプルです。詳しくは『Messaging APIドキュメント』の「",[33,94,96],{"href":95},"\u002Fdocs\u002Fmessaging-api\u002Fverify-webhook-signature\u002F","Webhookの署名を検証する","」に図つきの説明がありますが、ここでは要点だけかいつまんで紹介します。",[99,100,101,112,115],"ul",{},[102,103,104,105,108,109,111],"li",{},"LINEプラットフォームは、リクエストボディを入力データ、",[71,106,107],{},"チャネルシークレットをハッシュ鍵","として、HMAC-SHA256で署名を生成し、",[86,110,88],{},"ヘッダーにセットして送ってきます。",[102,113,114],{},"ボットサーバー側でも、受け取ったリクエストボディと、自分が管理しているチャネルシークレットを使って同じ計算を行い、署名を生成します。",[102,116,117],{},"両者が一致すれば、そのWebhookは「チャネルシークレットを知っている、つまりLINEプラットフォームから送信され、かつ改ざんもされていない」ものだと確認できます。",[26,119,120],{},"チャネルシークレットはLINEプラットフォームと開発者だけが知っている秘密鍵なので、これを知らない第三者は正しい署名を作れません。だからこそ、署名さえ検証していればなりすましを防げる、というわけです。",[26,122,123,124,127],{},"ひとつ注意点として、",[71,125,126],{},"署名を検証する前にリクエストボディを加工してはいけません","。デシリアライズや文字列の置換などをしてしまうと、計算結果が変わって検証に失敗します。受け取った生のボディのまま検証するようにしてください。",[45,129,131],{"id":130},"use-sdk","SDKの署名検証を活用しよう",[26,133,134,135,139,140,142],{},"各言語の",[33,136,138],{"href":137},"\u002Fdocs\u002Fmessaging-api\u002Fline-bot-sdk\u002F","LINE Messaging API SDK","には、署名検証のためのユーティリティが用意されています。チャネルシークレットとリクエストボディ、",[86,141,88],{},"ヘッダーを渡すだけで検証が完結します。",[26,144,145,146,149],{},"うれしいのは、",[71,147,148],{},"この署名検証の部分だけを切り出して使うこともできる","点です。Webアプリケーションのフレームワークや構成の都合でSDKをフルに導入しづらい場合でも、署名検証のユーティリティだけを利用する、といった使い方が可能です。自前でHMAC計算を実装してうっかりミス（たとえばアルゴリズムやエンコードの取り違え）をするより、用意されているものを使うほうが安全で確実です。ぜひ活用してください。",[45,151,153],{"id":152},"wrap-up","おわりに",[26,155,156],{},"Webhookの署名検証は、一度実装してしまえば普段はあまり意識しない地味な処理ですが、ボットサーバーのセキュリティを支える大事な土台です。",[26,158,159],{},"まだ署名検証を入れていないボットがあれば、これを機にぜひ導入してみてください。",[161,162],"tags",{"tags":163,"lang":164,"section":165},"messaging-api","en","tips",{"title":15,"searchDepth":167,"depth":167,"links":168},4,[169,173,176,179,182],{"id":170,"depth":171,"text":172},"webhookのなりすましという問題-webhook-spoofing",2,"Webhookのなりすましという問題 {#webhook-spoofing}",{"id":174,"depth":171,"text":175},"送信元ipアドレスで絞る方法とその限界-ip-allowlist","送信元IPアドレスで絞る方法とその限界 {#ip-allowlist}",{"id":177,"depth":171,"text":178},"署名を検証するという方法-signature-verification","署名を検証するという方法 {#signature-verification}",{"id":180,"depth":171,"text":181},"sdkの署名検証を活用しよう-use-sdk","SDKの署名検証を活用しよう {#use-sdk}",{"id":183,"depth":171,"text":184},"おわりに-wrap-up","おわりに {#wrap-up}","md",{"date":187,"tags":163,"locale":188,"sidebar":189},"2026-06-18 00:00 UTC","ja",false,true,"\u002Fja\u002Ftips\u002F2026\u002F06\u002F18\u002Fverify-webhook-signature",{"title":5,"description":28},"ja\u002Ftips\u002F2026\u002F06\u002F18\u002Fverify-webhook-signature","qoSZ_ewaW-BDapYggtpjYAkRiKRu5Gj6w0galCW_6qU",{"id":4,"title":5,"body":196,"description":28,"extension":185,"meta":289,"navigation":190,"path":191,"seo":290,"stem":193,"__hash__":194},{"type":7,"value":197,"toc":282},[198],[10,199,200,204,206,208,212,214,216,218,220,222,224,226,232,234,236,240,244,256,258,262,264,270,274,276,278,280],{},[13,201,202],{"id":15,"class":16},[18,203],{},[21,205],{"date":23,"class":24},[26,207,28],{},[26,209,31,210,37],{},[33,211,36],{"href":35},[26,213,40],{},[42,215],{},[45,217,48],{"id":47},[26,219,51],{},[26,221,54],{},[45,223,58],{"id":57},[26,225,61],{},[26,227,64,228,69,230,74],{},[33,229,68],{"href":67},[71,231,73],{},[26,233,77],{},[45,235,81],{"id":80},[26,237,84,238,89],{},[86,239,88],{},[26,241,92,242,97],{},[33,243,96],{"href":95},[99,245,246,252,254],{},[102,247,104,248,108,250,111],{},[71,249,107],{},[86,251,88],{},[102,253,114],{},[102,255,117],{},[26,257,120],{},[26,259,123,260,127],{},[71,261,126],{},[45,263,131],{"id":130},[26,265,134,266,139,268,142],{},[33,267,138],{"href":137},[86,269,88],{},[26,271,145,272,149],{},[71,273,148],{},[45,275,153],{"id":152},[26,277,156],{},[26,279,159],{},[161,281],{"tags":163,"lang":164,"section":165},{"title":15,"searchDepth":167,"depth":167,"links":283},[284,285,286,287,288],{"id":170,"depth":171,"text":172},{"id":174,"depth":171,"text":175},{"id":177,"depth":171,"text":178},{"id":180,"depth":171,"text":181},{"id":183,"depth":171,"text":184},{"date":187,"tags":163,"locale":188,"sidebar":189},{"title":5,"description":28},[292,293,294,295,296],{"id":47,"depth":171,"text":48},{"id":57,"depth":171,"text":58},{"id":80,"depth":171,"text":81},{"id":130,"depth":171,"text":131},{"id":152,"depth":171,"text":153},1782957697050]