💻
[esa] Generic webhookのSignatureをNode.jsでチェックする
はじめに
esaの Generic webhook には、リクエストボディの改竄を防ぐために Signatureを付加する仕組みがあります。
Generic webhookの設定画面で下図のように Secret (optional)
を設定しておくと、送信されるリクエストに X-Esa-Signature
というヘッダーが付加されます。
リクエストボディの内容から算出される MAC値 が X-Esa-Signature
の値と一致することをチェックすることによって、リクエストの内容が改竄されていないことを確認することができます。
チェックの手順
Signatureの機能を利用する場合、公式ドキュメント の説明に沿ってチェックします。手順としては以下のようになります。
- リクエストボディに対し、
Secret (optional)
に設定したシークレット文字列を共有キーとした HMAC 値(ハッシュ関数はSHA-265
)を算出 -
X-Esa-Signature
の中身がsha265=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f
のような文字列となっているので、sha265=
に続くMAC値の部分を上記で算出した値と比較する - 一致していればリクエストが改竄されていないことの証明になる
Node.jsでの実装例
ruby(rack)での実装例は 公式ドキュメント のとおりですが、Node.jsでの実装例をWebであまり見かけないので書いておきます。
例として zeit/micro を使った場合の具体的なコードは以下のようになります。
const {json, send} = require('micro')
const crypto = require('crypto')
module.exports = async (req, res) => {
const body = await json(req)
const signature = req.headers['x-esa-signature']
const computedSignature = 'sha256=' + crypto.createHmac('sha256', process.env.ESA_SECRET).update(JSON.stringify(body)).digest('hex')
if (signature !== computedSignature) {
send(res, 403, 'Invalid signature')
}
// ... your code
}
Node.js組み込みの Cryptoモジュール で提供されている Hmacクラス を使って実装しています。
crypto.createHmac('sha256', process.env.ESA_SECRET)
でシークレットとハッシュ関数を指定してHmacクラスのインスタンスを生成し、 .update(JSON.stringify(body))
でリクエストボディの内容を渡し、 .digest('hex')
で実際のMAC値を算出しています。
ちなみにNode.jsでは ヘッダーのキーはすべて小文字に変換されています。
See also
拙作の esa2github に 実際のコードがある ので、あわせて参考にしてみてください✋
Discussion