- 印刷
- ダークライト
はじめに
Webhook URLを設定した場合、そのURLへのリクエストが実際にAutifyから送られたものかどうかを確認するためにWebhookシークレットも併せて設定することを推奨します。このドキュメントではWebhookシークレットがない場合のリスクや、生成・設定・確認方法について説明します。
Webhookシークレットとは
Webhook URLはインターネット上に公開する必要があります。そのため、そのURLに対するリクエストが、常にAutifyから送信されているという保証はどこにもありません。また、中間者攻撃などにより、Webhookのリクエストが改ざんされてしまう可能性もあります。
これらのリスクに対応するために、ユーザーはWebhookシークレットを設定することができます。Webhookシークレットとは、受け取ったWebhookリクエストを検証するために、ユーザーとAutifyだけが共有する任意の文字列(秘密鍵)のことです。この値はユーザーが作成してAutifyに設定する必要があります。
Autifyとユーザーは、同じWebhookシークレットを使って、同じアルゴリズムで ペイロード(ここではHTTPリクエストのボディ全文を指します)の HMAC (暗号化された文字列)を生成します。この2つを比較することで、ユーザーは以下のことを確認することができます。
- リクエストの送信元が、Webhookシークレットを 知っている こと。Webhookシークレットを、Autifyとユーザー以外の第三者に誤って公開しない限り、受信したリクエストが間違いなくAutifyからのものであると確認できます。
- 送信されたリクエストが 改ざんされたものでない こと。HMACは、リクエストを送信した時点で生成されたものなので、リクエストが送信されてからWebhook URLに届くまでの経路で改ざんされていないと確認できます。
Webhookシークレットを使ったリクエストの検証
Webhookシークレットを設定すると、AutifyからのリクエストヘッダにX-Autify-Signature
が含まれます。この値は、AutifyがリクエストとWebhookシークレットを用いて生成したハッシュ値です。
Webhookシークレットの生成及び設定方法
- ワークスペース設定ページのWebhookセクションで新規作成、もしくは既存のものを選択
- Webhookシークレットを生成。値はランダムに生成するなど、推測されにくいものを使用してください。以下は、コマンドラインによるランダム値の生成方法です。
# ランダム値の生成例
$ openssl rand -hex 20b2f82af62f9980f6b01e1cd7e716230d0a063f58
- Webhookシークレットに前のステップで取得した値を入力
- 作成または更新ボタンを押下
Webhookシークレットを用いたペイロードの検証方法
以下のように、ペイロードのHMACを生成し、リクエストヘッダに記載されたものと比較します。
- Webhookシークレットを用いてペイロードのHMACを計算
- (1)で取得した値を
sha1=
の末尾に結合 - 受け取ったリクエストヘッダ
X-Autify-Signature
の値を取得 - (2)と(3)で取得した値を比較して一致することを確認
- ※ 値が一致しない場合、Autify以外からのリクエストの可能性があります。
以下は各言語での確認方法のサンプルです:
Ruby
$ gem install rack
require 'openssl'
require 'rack'
digest = OpenSSL::HMAC.hexdigest(
OpenSSL::Digest.new('sha1'),
'<Webhookシークレット>',
'<ペイロード>'
)
computed_signature = "sha1=${digest}"
request_signature = '<X-Autify-Signature>'
Rack::Utils.secure_compare(
computed_signature,
request_signature
)
Node.js
const crypto = require('crypto');
const digest = crypto
.createHmac('sha1','<Webhookシークレット>')
.update('<ペイロード>')
.digest('hex')
const computedSignature = `sha1=${digest}`
const requestSignature = '<X-Autify-Signature>'
crypto.timingSafeEqual(
Buffer.from(computedSignature, 'utf8'),
Buffer.from(requestSignature, 'utf8')
)