Messaging APIのWebhookの署名を検証しよう
こんにちは。Messaging APIの開発を担当している奥薗(@mokuzon)です。
Messaging APIでボットを作るとき、ユーザーからのメッセージや友だち追加といったイベントは、Webhookとしてみなさんのボットサーバーに届きます。このWebhookを受け取って処理するのがボット開発の出発点なわけですが、ここで一度立ち止まって考えておきたいことがあります。「そのリクエスト、本当にLINEプラットフォームから来たものですか?」という点です。
この記事では、Webhookのなりすまし対策として署名を検証しようという話を、簡単に紹介します。
Webhookのなりすましという問題
WebhookはHTTPSのPOSTリクエストとして届きます。裏を返すと、ボットサーバーのエンドポイントのURLさえ分かってしまえば、LINEプラットフォーム以外の第三者でも同じ形のリクエストを送れてしまう、ということでもあります。
もし署名を検証せず、届いたリクエストの中身をそのまま信じて処理してしまうと、攻撃者がでっち上げた偽のイベントを、本物のユーザーからのイベントとして処理してしまうおそれがあります。だからこそ、「届いたWebhookが本当にLINEプラットフォームから送られたものか」を確認する仕組みが必要になります。
送信元IPアドレスで絞る方法とその限界
「送信元が正しいか確認したい」と言われて、まず思いつくのは送信元IPアドレスでアクセスを制限する方法かもしれません。LINEプラットフォームのIPアドレス以外からのアクセスをブロックすれば、たしかになりすましは防げそうに見えます。
しかし、実際にはこの方法は採用できません。Messaging API開発ガイドラインにも書かれているとおり、LINEプラットフォームのIPアドレスは開示しておらず、また予告なく変更される場合があるためです。許可リストに登録すべきIPアドレスがそもそもわからないため、IPアドレスによるアクセス制限は実現できません。
ではどうするか。ここで登場するのが署名です。
署名を検証するという方法
LINEプラットフォームは、Webhookを送信するときにリクエストヘッダーx-line-signatureに「署名」を付けています。この署名を検証することで、IPアドレスに頼らずに送信元の正当性を確認できます。
仕組みはとてもシンプルです。詳しくは『Messaging APIドキュメント』の「Webhookの署名を検証する」に図つきの説明がありますが、ここでは要点だけかいつまんで紹介します。
- LINEプラットフォームは、リクエストボディを入力データ、チャネルシークレットをハッシュ鍵として、HMAC-SHA256で署名を生成し、
x-line-signatureヘッダーにセットして送ってきます。 - ボットサーバー側でも、受け取ったリクエストボディと、自分が管理しているチャネルシークレットを使って同じ計算を行い、署名を生成します。
- 両者が一致すれば、そのWebhookは「チャネルシークレットを知っている、つまりLINEプラットフォームから送信され、かつ改ざんもされていない」ものだと確認できます。
チャネルシークレットはLINEプラットフォームと開発者だけが知っている秘密鍵なので、これを知らない第三者は正しい署名を作れません。だからこそ、署名さえ検証していればなりすましを防げる、というわけです。
ひとつ注意点として、署名を検証する前にリクエストボディを加工してはいけません。デシリアライズや文字列の置換などをしてしまうと、計算結果が変わって検証に失敗します。受け取った生のボディのまま検証するようにしてください。
SDKの署名検証を活用しよう
各言語のLINE Messaging API SDKには、署名検証のためのユーティリティが用意されています。チャネルシークレットとリクエストボディ、x-line-signatureヘッダーを渡すだけで検証が完結します。
うれしいのは、この署名検証の部分だけを切り出して使うこともできる点です。Webアプリケーションのフレームワークや構成の都合でSDKをフルに導入しづらい場合でも、署名検証のユーティリティだけを利用する、といった使い方が可能です。自前でHMAC計算を実装してうっかりミス(たとえばアルゴリズムやエンコードの取り違え)をするより、用意されているものを使うほうが安全で確実です。ぜひ活用してください。
おわりに
Webhookの署名検証は、一度実装してしまえば普段はあまり意識しない地味な処理ですが、ボットサーバーのセキュリティを支える大事な土台です。
まだ署名検証を入れていないボットがあれば、これを機にぜひ導入してみてください。