前回の記事「受信メッセージの改ざんチェックをする」で Signature を使った改ざんチェックを紹介したのですが、その後いろいろ試していると、データ内に &
(アンパサント)や >
(グレーターザン)などの記号が入るケースでチェックが正常に機能しなくなることがありました。
これはいったいどういうことだ??
まぁ、きっと LINEWORKS の開発が escape 処理をし損ねたんだろう。
まったく、お茶目さんめ(*‘∀‘)
なーんて思っていたら、Java だと &
などの記号が入ってても問題ないよ!って連絡が。
Σ( ゚Д゚)!
そして、別のエンジニアからも Go 言語で問題なくチェックできたよ!って連絡が!
ΣΣ( ゚Д゚)!!
・・・あっれぇー?もしかして、javascript がいけないのかな?
それとも、express ??
ってか私の手順がおかしい!?なんてこった!(; ・`д・´)
LINEWORKS サーバ側の動き
改ざんチェック用の X-Works-Signature
は body
の内容をエンコードしたものです。
なので、送られてきた body
の内容を手順通りにエンコードすれば X-Works-Signature
と一致します。
一致しなければデータが改ざんされているということです。(ここまで前回のおさらい)
LINEWORKS サーバは &
やいくつかの記号については送信する際に escape 処理をしています。
まぁ、当然ですよね。escape 処理をしないと意図していない動きをしてしまう可能性がありますから。
Unicode
なので &
は escape 処理されて \u0026
となります。
なので、&
が body
内に含まれていると \u0026
に escape 処理された上でエンコードされ X-Works-Signature
が生成されます。
そして、escape 処理した &
を含む body
と X-Works-Signature
がサーバから送信されます。
ここまでは何も問題ないですね。
シグナル、オールグリーンです。
では、次に express
の動きを見ていきましょう。
受信サーバ側の動き
LINEWORKS から送信されてくるデータは JSON 形式になります。
受信サーバでは express
データ解析して JSON オブジェクトとして受け取ります。
crypto
で作った hmac
は buffer
または string
でないと update
できないので、JSON.stringify
して string
に変換しました。
app.post('/callback', (req, res) => {
const reqbody = req.body;
const crypto = require('crypto');
const hmac= crypto.createHmac('sha256', process.env.API_ID);
hmac.update(JSON.stringify(req.body));
const data=hmac.digest('base64');
.......
んで、ここからは定かではないのですが、どちらかの時点で &
の \u0026
は unescape 処理されてしまってるようなんですよ。
- JSON オブジェクトとして受け取った時点で unescape 処理している
-
JSON.stringify
した時点で unescape 処理している
おそらく 1 だと思うのですが、確証が得られなかったので。。。
どなたかご存じでしたら教えてください(>_<)
express.json({verify:}) を使う
では、どうしたらいいかという話ですが、タイトル通り verify
という機能を使うようです。
・・・express
はとても便利なフレームワークです!(いまさら)
便利すぎて不勉強だった私は、JSON 形式のデータを受け取る際は昔習った通りのおまじないをしていました。
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
おまじないと言われたので今まで特に気にしなかったのですが、先に述べた受信リクエストを解析して、JSON 形式のものは JSON オブジェクトとして成形する動きは body-parser
さんはがしてくれていたようです。
勉強不足が悔やまれます(; ・`д・´)
しかも、express
の公式のドキュメントを読んでいて気付いたのですが、わざわざ body-parser
さんをインストールしなくても express.json
を使えば body-parser
さんに基づいた設定が可能なんだそうです。なんてこった!
(公式)express.json([options])
ってなわけでちょっとコードを修正します。
const express = require('express');
const app = express();
app.use(express.json());
body-parser
さん、サヨウナラ。また会う日まで( ノД`)
話が逸れましたので戻します。
受信したデータのチェックには、express.json
のプロパティである verify
を使うことが推奨されているそうです。
まったく知りませんでした!ヾ(´∀`)ノ
app.use
で設定することにより、受信したデータのチェックを常に行うことができます。
引数の buf
には request body
の buffer
データが入っているので hmac.update
でそのまま使用することができます。
さらに!エラーの場合には throw
することにより処理を中断することもできます!
なんて便利!( ゚Д゚)
以下、最終的なコードになります。
const express = require('express');
const app = express();
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"];
// signature が一致しない場合にはエラー処理をする
if (data!=signature) throw 'NOT_MATCHED';
}}));
app.post('/callback', (req, res) => {
// signature が一致したときだけ callback の処理が始まる
});
これで、&
や他の記号などが escape されていても大丈夫!
正しく改ざんチェックを行うことができます。やったね!
#おわりに
ここまでお付き合いいただきありがとうございました。
前回の記事を修正するか、新しく記事を書くか悩んだのですが、新しく記事を書いてしまいました。
いや、前回の記事も修正した方がいいな、うん。
あとで修正しよう。
express
、本当にまだまだ知らない機能が盛りだくさんです。
フレームワークと呼ばれるものでまともに使っているのは express
くらいなので、もっとしっかり勉強しようと思います。
ではまた!(^^)/
#参考にさせていただきましたm(_ _)m