LoginSignup
30
22

More than 5 years have passed since last update.

LINE Messaging APIでX-Line-Signatureの署名検証を行う(AzureFunctions/Node.js)

Last updated at Posted at 2016-10-13

はじめに

前回、Azure Functionsを使ってLINE Messaging APIを使ったbotを作成しました。
Azure Functionsを使って非同期処理のLINE BOTを作成する
スクリーンショット 2016-10-13 1.16.32.png

前回の残作業として、以下の内容が残っていました。

紹介したコードは最小限のロジックのみ実装していてセキュリティやエラー処理などは考慮していません。
例えば、LINEサーバーからのメッセージであることの検証などが不足しています。

今回はメッセージの署名検証を行ってみます。

署名検証について

LINE developersのAPI Referenceに次の記述があります。

Webhook Authentication

X-Line-Signature Request Headerに入っているSignatureを検証することによって、
リクエストがLINE Platformから送信されたものであることを確認する必要があります。

検証は以下の手順で行います。

1. Channel Secretを秘密鍵として、HMAC-SHA256アルゴリズムによりRequest Bodyのダイジェスト値を得る。
2. ダイジェスト値をBASE64エンコードした文字列が、Request Headerに付与されたSignatureと一致することを確認する。

つまり、用意したWebhookへのアクセスが本当にLINEのサーバーからのそれなのかチェックする必要があります。
検証手順も記載されているのでその通りに実装して見ます。

実装

Webを検索すると類似の実装をやっている例がありました。
【乗るしかない】AWS LambdaとLINE BOT APIでRSSを通知する【このビッグウェーry】 | Developers.IO

昔のLINE APIを使う際の認証のためのコードのようですが、やっていることはほとんど同じです。上記のサイトを参考にして実装したのがこちらです。

var crypto = require("crypto");

function validate_signature(signature, body)
{
    const LINE_CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET;
    return signature == crypto.createHmac('sha256', LINE_CHANNEL_SECRET).update(Buffer.from(JSON.stringify(body))).digest('base64');
}

module.exports = function(context, req) {
    context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);

    if (validate_signature(req.headers['x-line-signature'], req.body)) {
        context.bindings.outputQueueItem = req.body;
    }
    else {
        context.log('fail to validate signature');
    }

    context.res = { body : "" };
    context.done();
};

yorifuji/azure-functions-line-signature-bot

個別に見ていきます。

module.exports = function(context, req) {
    context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);

    if (validate_signature(req.headers['x-line-signature'], req.body)) {
        context.bindings.outputQueueItem = req.body;
    }
    else {
        context.log('fail to validate signature');
    }

    context.res = { body : "" };
    context.done();
};

まずLINEサーバーからのリクエストが届くと、こちらの関数が実行されます。
署名検証のために新たに追加したのはこちらのコードです。

    if (validate_signature(req.headers['x-line-signature'], req.body)) {
        context.bindings.outputQueueItem = req.body;
    }
    else {
        context.log('fail to validate signature');
    }

validate_signatureにreq.headers['x-line-signature']とreq.bodyを渡しています。
req.headers['x-line-signature']にはLINEサーバから付与されたSignatureが入っています。
req.bodyはRequest Bodyそのものです(JSON)。

次にvalidate_signatureの実装はこちらです。

var crypto = require("crypto");

function validate_signature(signature, body) {
    const LINE_CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET;
    return signature == crypto.createHmac('sha256', LINE_CHANNEL_SECRET).update(Buffer.from(JSON.stringify(body))).digest('base64');
}

先頭の var crypto... はNode.jsのcryptoを利用するための宣言です。HMAC-SHA256を求めるのに利用します。
validate_signatureの先頭でLINE_CHANNEL_SECRETを環境変数(process.env)から読み込んでいます。

    const LINE_CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET;

これはHMAC-SHA256の秘密鍵として利用します。LINE_CHANNEL_SECRETの値はLINE Developersの管理画面に表示されるのでそちらからコピーしておきます。
環境変数から読み込んでいるのはGitHub上でコードを管理して、それを直接Azureにデプロイする運用にしているためです。

署名検証の手順としては、crypto.createHmacで生成したインスタンスに対してupdate関数を使ってbodyの内容をセットします。最後にBASE64に変換した値を出力します。
出力した値とsignatureが一致していれば署名検証に成功です。

サンプルコード

GitHubはこちらです。yorifuji/azure-functions-line-signature-bot

前回と同様に、Azure Functionsで「継続的インテグレーションの構成」の設定を行なっている方は、リポジトリをこちらに切り替えていただくとソースコードを丸ごと切り替えることができます。

なお、署名検証のために以下のキーを環境変数に登録する必要があります。

キー:
LINE_CHANNEL_SECRET
値:
LINE developersのChannel Secretの値

まとめ

LINE Messaging APIでX-Line-Signatureの署名検証を行いました。内容に誤りがあればコメントいただけると幸いです。

予定

  • メッセージの種類に応じて応答を変える
30
22
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
22