演習 - シークレットを使用して Webhook ペイロードをセキュリティで保護する
この演習では、シークレットを使って Webhook のペイロードをセキュリティで保護し、また Azure Function を使ってペイロードが実際に GitHub からのものであることを検証する方法を学習します。
Azure 関数のキーを取得する
Azure portal で、モジュールの最初の演習で作成した関数アプリに戻ります。
左側のメニュー ペインの [関数] で、[関数] を選択します。 "関数アプリ" の [関数] ペインが表示されます。
作成した HttpTrigger1 を選択します。 "関数" の [HtttpTrigger1] ペインが表示されます。
左側のメニュー ウィンドウの [開発者] で、[コードとテスト] を選択します。 "関数" の [コード + テスト] ペインが表示されます。
関数の index.js JavaScript ファイルで、ファイルの先頭の
module.exports
ステートメントの上に crypto-js ライブラリへの参照を追加します。const Crypto = require('crypto');
上部のメニュー バーで、[保存] を選択します。 ペインの下部に [ログ] ペインが表示されます。
左側のメニュー ウィンドウの [開発者] で、[関数キー] を選択します。 "関数" の [Function Keys](関数キー) ペインが表示されます。
[値] 列で、[値の表示] リンクを選択します。
[クリップボードにコピー] アイコンを選択し、次の手順で使用するためにこのキーを保存します。
左側のメニュー ウィンドウの [開発者] で、[コードとテスト] を選択します。 "関数" の [コード + テスト] ペインが表示されます。
コード ブロックの
context.log
ステートメントの後に、次のコードを追加します。 "<既定のキー>" は、クリップボードにコピーしたばかりの既定のキーに置き換えます。const hmac = Crypto.createHmac("sha1", "<default key>"); const signature = hmac.update(JSON.stringify(req.body)).digest('hex');
このコードでは、GitHub と同じメカニズムを使用してキーのハッシュを計算します。
要求ヘッダーの
x-hub-signature
の形式と一致するように、キーの先頭の前にsha1=
を追加する別のconst
を追加します。 関数に次のコードを追加します。const shaSignature = `sha1=${signature}`;
次のコードを追加して、要求ヘッダーから GitHub の署名を取得します。
const gitHubSignature = req.headers['x-hub-signature'];
2 つの文字列を比較します。 一致する場合は、次のように要求を処理します。
if (!shaSignature.localeCompare(gitHubSignature)) { // Existing code if (req.body.pages[0].title) { ... } else { ... } }
文字列が一致しない場合は、署名が一致しないことを送信者に知らせるメッセージと共に、HTTP 401 (Unauthorized) 応答が返されます。
if (!shaSignature.localeCompare(gitHubSignature)) { ... } else { context.res = { status: 401, body: "Signatures don't match" }; }
完成した関数は次のようになります。
const Crypto = require('crypto'); module.exports = async function (context, req) { context.log('JavaScript HTTP trigger function processed a request.'); const hmac = Crypto.createHmac("sha1", "<default key>"); const signature = hmac.update(JSON.stringify(req.body)).digest('hex'); const shaSignature = `sha1=${signature}`; const gitHubSignature = req.headers['x-hub-signature']; if (!shaSignature.localeCompare(gitHubSignature)) { if (req.body.pages[0].title) { context.res = { body: "Page is " + req.body.pages[0].title + ", Action is " + req.body.pages[0].action + ", Event Type is " + req.headers['x-github-event'] }; } else { context.res = { status: 400, body: ("Invalid payload for Wiki event") } } } else { context.res = { status: 401, body: "Signatures don't match" }; } };
上部のメニュー バーで、[保存] を選択します。 "接続完了" ステートメントと共に [ログ] ペインが表示されます。
Webhook のシークレットを更新する
GitHub ポータルで GitHub アカウントに切り替えます。
リポジトリを選択します。
上部のメニュー バーで、[Settings](設定) を選択します。 [設定] ペインが表示されます。
サイドバーで、[Webhooks] を選択します。 [Webhook] ペインが表示されます。
Webhook の横にある [Edit](編集) を選択します。
[シークレット] テキスト ボックスに、この演習で先ほど保存した関数の既定のキーを入力します。
ページの下部までスクロールし、[Update webhook](webhook の更新) を選択します。 [Webhooks/Manage webhooks](Webhook/Webhook の管理) ペインが表示されます。
Webhook と Azure 関数をテストする
[Recent Deliveries](最近のデリバリ) タブを選択します。
省略記号 ([...]) ボタンを選択して最新の (一番上の) 配信エントリを選択します。
[Redeliver](再配信) を選択します。 [Redeliver payload?](ペイロードを再配信しますか?) ボックスで [Yes, redeliver this payload](はい、このペイロードを再配信します) を選択します。
この操作で、Wiki ページの編集がもう一度シミュレートされます。
省略記号 ([...]) ボタンを選択して最新の (一番上の) 配信エントリを選択します。
ヘッダー セクションに、
x-hub-signature
が表示されます。 また、応答コードが 200 であることもわかります。これは、要求が正常に処理されたことを示します。Request URL: https://testwh123456.azurewebsites.net/api/HttpTrigger1?code=aUjXIpqdJ0ZHPQuB0SzFegxGJu0nAXmsQBnmkCpJ6RYxleRaoxJ8cQ%3D%3D Request method: POST content-type: application/json Expect: User-Agent: GitHub-Hookshot/16496cb X-GitHub-Delivery: ce122460-6aae-11e9-99d4-de6a298a424a X-GitHub-Event: gollum X-Hub-Signature: sha1=<hash of default key>
無効な署名をテストする
GitHub ポータルの webhook ページで、[Settings](設定) タブを選択します。
[Secret](シークレット) テスト ボックスで、[Change Secret](シークレットの変更) を選択します。
ランダムな文字列を入力し、下にスクロールして、[Update webhook](Webhook の更新) をクリックします。
Webhook によって使用されているキーは、Azure 関数で想定されているものと一致しなくなります。
[Recent Deliveries](最近のデリバリ) タブを選択します。
省略記号 ([...]) ボタンを選択して最新の (一番上の) 配信エントリを選択します。
[Redeliver](再配信) を選択し、[Redeliver payload?](ペイロードを再配信しますか?) ボックスで [Yes, redeliver this payload](はい、このペイロードを再配信します) を選択します。
今回は、応答コードが 401 と表示されます。これは、要求が承認されなかったことを示します。
省略記号ボタン ([...]) を選択して最新の (一番上の) 配信エントリ ("再配信") を選択します。
[Response](応答) タブを選択し、[Body](本文) セクションに "Signatures don't match" (署名が一致しません) というメッセージが応答の本文として表示されることを確認します。