メッセージが通信途中で奪われ、改ざんされて送られてきたら大変ですよね。
漫画やアニメだとハッカーやクラッカーのみなさんがカタカタやって偽のメッセージで誘導したりしています。
現実でもやられてしまっては大変です!
子どものふりをして送金用の QR コードが送られてくるかもしれません。
友人を装って「ばーか」なんて送ってくるかもしれません。
待ち合わせ場所に行ったら待ち惚けを食らうかもしれません。
お茶しに行ったら変な宗教の勧誘かもしれません。
偽メッセージ許すまじ。(; ・`д・´)
そんなことにならないためにも、正しいメッセージが送られてきているのか、しっかり改ざんチェックをしましょう!(=゚ω゚)ノ
Signature を使った改ざんチェック
ここで紹介するのは Signature を使った改ざんチェックです。
御馴染み(?)の LINEWORKS トーク BOT がメッセージを受け取る際に改ざんチェックをしてみます。
プログラムは機械的に処理するので、改ざんメッセージなんか送られて来たらたまったものではありません。
怪しいメッセージは弾く弾く弾く!( `ー´)ノ
そのために、しっかりチェックしていきましょう。
公式ドキュメントには、以下のように書かれています。
LINEWORKS メッセージサーバーから送信されたメッセージの改ざん有無を確認するには、ヘッダーに含まれる X-WORKS-Signature を用います。
https://developers.worksmobile.com/jp/document/1005009?lang=ja
- API ID を秘密鍵として利用し、メッセージサーバーから送られた body の内容を HMAC-SHA256 アルゴリズムでエンコードします。
- 上記の HMAC-SHA256 アルゴリズムでエンコードした結果を BASE64 エンコードします。
- X-WORKS-Signature のヘッダー値と比較し、同一であればメッセージは改ざんされていないと判断できます。
node.js
にはライブラリが揃っているので、実現はそんなに難しくありません。
さっそく実装していきましょう!
改ざんチェック用のコード
フレームワークは Express を使います。
あと、body-parser ライブラリもインストールします。
express.json で設定した方が良いことがわかりましたので、コードも修正しました(2019/09/24)
詳しい解説は Express での改ざんチェックには verify を使う にて。
$ npm install express
インストールしたらコードを書いていきます。
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 モジュールを使います。
crypto モジュールは Node.js に標準装備されていますので、Node.js をインストールしてあれば使用することができます。
crypto.createHmac
関数を使用してエンコードします。
第一引数にアルゴリズムを指定して、第二引数には秘密鍵として API ID を指定します。
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', API_ID);
エンコードした値を .digest('base64')
で BASE64 エンコードします。
const data = hmac.digest('base64');
これで、値が HMAC-SHA256 アルゴリズムでエンコードされ、さらに BASE64 でエンコードでされます。
#おわりに
ここまでお付き合いいただきありがとうございました。
Webhook(LINEWORKS だと Callback)のセキュリティとして、正当な送信元かどうかを判断するためには、今回紹介した signature を使う方法と送信元 IP を特定しておく方法の2つがあるみたいです。
IP はサービス側で公開していたり、していなかったりします。
LINEWORKS では、現在は公開していないみたいですね。
Qiita Team では公開しています。
Qiita Team Webhook の送信元 IP アドレス
ただ、IP アドレスは変更になる可能性があります。
GitHub なんかは、そのことを考慮してそのときの IP を取得する API を用意しています。
GitHub Meta API
こんなところでも API が活躍を!ヾ(´∀`)ノ
API を触り始めて一年が経とうとしておりますが、まだまだ知らないことがたくさんあります。
楽しいですねー。
ではまた!(^^)/
#参考にさせていただきましたm(_ _)m
LINEWORKS メッセージ受信 - Callback 形式
Node.jsで暗号化とハッシュ
安全で便利な Webhook を作る