受信メッセージの改ざんチェックをする

メッセージが通信途中で奪われ、改ざんされて送られてきたら大変ですよね。
漫画やアニメだとハッカーやクラッカーのみなさんがカタカタやって偽のメッセージで誘導したりしています。
現実でもやられてしまっては大変です！

子どものふりをして送金用の QR コードが送られてくるかもしれません。
友人を装って「ばーか」なんて送ってくるかもしれません。
待ち合わせ場所に行ったら待ち惚けを食らうかもしれません。
お茶しに行ったら変な宗教の勧誘かもしれません。

偽メッセージ許すまじ。(; ･`д･´)

そんなことにならないためにも、正しいメッセージが送られてきているのか、しっかり改ざんチェックをしましょう！(=ﾟωﾟ)ﾉ

## Signature を使った改ざんチェック

ここで紹介するのは Signature を使った改ざんチェックです。
御馴染み（？）の LINEWORKS トーク BOT がメッセージを受け取る際に改ざんチェックをしてみます。

プログラムは機械的に処理するので、改ざんメッセージなんか送られて来たらたまったものではありません。
怪しいメッセージは弾く弾く弾く！( ｀ー´)ノ
そのために、しっかりチェックしていきましょう。

公式ドキュメントには、以下のように書かれています。

LINEWORKS メッセージサーバーから送信されたメッセージの改ざん有無を確認するには、ヘッダーに含まれる X-WORKS-Signature を用います。
https://developers.worksmobile.com/jp/document/1005009?lang=ja

1. API ID を秘密鍵として利用し、メッセージサーバーから送られた body の内容を HMAC-SHA256 アルゴリズムでエンコードします。
2. 上記の HMAC-SHA256 アルゴリズムでエンコードした結果を BASE64 エンコードします。
3. X-WORKS-Signature のヘッダー値と比較し、同一であればメッセージは改ざんされていないと判断できます。

`node.js` にはライブラリが揃っているので、実現はそんなに難しくありません。
さっそく実装していきましょう！

## 改ざんチェック用のコード

フレームワークは [Express](http://expressjs.com/ja/) を使います。
~~あと、[body-parser](https://www.npmjs.com/package/body-parser) ライブラリもインストールします。~~
express.json で設定した方が良いことがわかりましたので、コードも修正しました（2019/09/24)
詳しい解説は [Express での改ざんチェックには verify を使う](https://qiita.com/kunihiros/items/d1dd0c5185417746a56b) にて。

```
$ npm install express
```

インストールしたらコードを書いていきます。

```js:app.js
const express = require('express');
const app = express();
const port = 8000;

app.use(express.json({verify:(req, res, buf, encoding) => {
    const crypto = require('crypto');
    const hmac= crypto.createHmac('sha256', API_ID);
    hmac.update(buf);
    
    const data=hmac.digest('base64');
    const signature=req.headers["x-works-signature"];

    console.log("body : " + data);
    console.log("head : " + signature);
    console.log("signature macth : " + (data == signature));
    
    if (data!=signature) throw 'NOT_MATCHED';
}}));

app.post('/callback', (req, res) => {
    // signature が一致したときだけ callback の処理が始まる
});

app.listen(port);
```

起動させて、Bot にメッセージを送信するとコンソールに改ざんチェックの結果が表示されます。

```
body : WciWOAmEO4irTUsBxSi2rSDXkQAIJJqQWjDyQdD5Q+U=
head : WciWOAmEO4irTUsBxSi2rSDXkQAIJJqQWjDyQdD5Q+U=
signature macth : true
```

正しいメッセージが送られてきていますね！
これで安心して、メッセージを処理できます。

ちなみに、メッセージが改ざんされているときはこんな感じです。

```
body : I54GxfBJ54ewbdMMYr8A0uW2W4jqzp52Pgqq1T3X0tU=
head : vb6ICyTsEeTthlCzCRJ0R9Iy0w8KiA+qB3C7kzSndpE=
signature macth : false
```

true or false の真偽値を使って、正しいメッセージだけ処理していきましょう♪

## HMAC-SHA256 アルゴリズムでエンコードするには crypto を使う

ちょっとだけコードの解説をば。

見出しの通り、HMAC-SHA256 アルゴリズムでエンコードするには [crypto](https://nodejs.org/api/crypto.html) モジュールを使います。
crypto モジュールは Node.js に標準装備されていますので、Node.js をインストールしてあれば使用することができます。
 
`crypto.createHmac` 関数を使用してエンコードします。
第一引数にアルゴリズムを指定して、第二引数には秘密鍵として API ID を指定します。

```js
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', API_ID);
```

エンコードした値を `.digest('base64')` で BASE64 エンコードします。

```js
const data = hmac.digest('base64');
```

これで、値が HMAC-SHA256 アルゴリズムでエンコードされ、さらに BASE64 でエンコードでされます。

#おわりに
ここまでお付き合いいただきありがとうございました。

Webhook（LINEWORKS だと Callback）のセキュリティとして、正当な送信元かどうかを判断するためには、今回紹介した signature を使う方法と送信元 IP を特定しておく方法の２つがあるみたいです。

IP はサービス側で公開していたり、していなかったりします。
LINEWORKS では、現在は公開していないみたいですね。

Qiita Team では公開しています。
[Qiita Team Webhook の送信元 IP アドレス](https://teams.qiita.com/help/security/qiita-team-webhook-ip-address/)

ただ、IP アドレスは変更になる可能性があります。
GitHub なんかは、そのことを考慮してそのときの IP を取得する API を用意しています。
[GitHub Meta API](https://developer.github.com/v3/meta/)

こんなところでも API が活躍を！ヾ(*´∀｀*)ﾉ
API を触り始めて一年が経とうとしておりますが、まだまだ知らないことがたくさんあります。
楽しいですねー。

ではまた！(^^)/

#参考にさせていただきましたm(_ _)m

[LINEWORKS メッセージ受信 - Callback 形式](https://developers.worksmobile.com/jp/document/1005009?lang=ja)
[Node.jsで暗号化とハッシュ](https://qiita.com/_daisuke/items/990513e89ca169e9c4ad)
[安全で便利な Webhook を作る](https://tech.degica.com/ja/2015/04/17/webhook/)