Express での改ざんチェックには verify を使う

[前回の記事「受信メッセージの改ざんチェックをする」](https://qiita.com/kunihiros/items/c4db8c2083e2d0b1e384)で 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` に変換しました。

```js
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 処理されてしまってるようなんですよ。

1. JSON オブジェクトとして受け取った時点で unescape 処理している
2. `JSON.stringify` した時点で unescape 処理している

おそらく 1 だと思うのですが、確証が得られなかったので。。。
どなたかご存じでしたら教えてください(>_<)

## express.json({verify:}) を使う

では、どうしたらいいかという話ですが、タイトル通り `verify` という機能を使うようです。

・・・`express` はとても便利なフレームワークです！（いまさら）
便利すぎて不勉強だった私は、JSON 形式のデータを受け取る際は昔習った通りのおまじないをしていました。

```js
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])](http://expressjs.com/ja/4x/api.html#express.json)

ってなわけでちょっとコードを修正します。

```js
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` することにより処理を中断することもできます！
なんて便利！( ﾟДﾟ)

以下、最終的なコードになります。

```js:app.js
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

[（公式）express.json([options])](http://expressjs.com/ja/4x/api.html#express.json)