💻

[esa] Generic webhookのSignatureをNode.jsでチェックする

2020/05/24に公開

はじめに

esaの Generic webhook には、リクエストボディの改竄を防ぐために Signatureを付加する仕組みがあります

Generic webhookの設定画面で下図のように Secret (optional) を設定しておくと、送信されるリクエストに X-Esa-Signature というヘッダーが付加されます。

リクエストボディの内容から算出される MAC値X-Esa-Signature の値と一致することをチェックすることによって、リクエストの内容が改竄されていないことを確認することができます。

チェックの手順

Signatureの機能を利用する場合、公式ドキュメント の説明に沿ってチェックします。手順としては以下のようになります。

  1. リクエストボディに対し、 Secret (optional) に設定したシークレット文字列を共有キーとした HMAC 値(ハッシュ関数は SHA-265 )を算出
  2. X-Esa-Signature の中身が sha265=5c2cd89667226539b34ea930781b8bf3e7f1236da7624eee6b748cd52899f91f のような文字列となっているので、 sha265= に続くMAC値の部分を上記で算出した値と比較する
  3. 一致していればリクエストが改竄されていないことの証明になる

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実際のコードがある ので、あわせて参考にしてみてください✋

GitHubで編集を提案

Discussion