{"meta":{"title":"Webhook 配信を検証する","intro":"webhook シークレットを使用して、webhook の配信が GitHubから行われるかどうかを確認できます。","product":"Webhooks","breadcrumbs":[{"href":"/ja/webhooks","title":"Webhooks"},{"href":"/ja/webhooks/using-webhooks","title":"Webhook の使用"},{"href":"/ja/webhooks/using-webhooks/validating-webhook-deliveries","title":"配信を検証する"}],"documentType":"article"},"body":"# Webhook 配信を検証する\n\nwebhook シークレットを使用して、webhook の配信が GitHubから行われるかどうかを確認できます。\n\n## Webhook 配信の検証について\n\nサーバーがペイロードを受信できるように設定されると、設定したエンドポイントに送信されるすべての配信を待ち構えるようになります。 サーバーが、 GitHub によって送信された Webhook 配信のみを処理し、配信が改ざんされていないことを確認するには、配信をさらに処理する前に webhook 署名を検証する必要があります。 これにより、GitHub からのものではない配信の処理にサーバー時間を費やすのを防ぎ、中間者攻撃の防止にも役立ちます。\n\nそのためには、次の手順を実行する必要があります。\n\n1. Webhook のシークレット トークンを作成します。\n2. トークンをサーバーに安全に格納します。\n3. 受信 webhook ペイロードをトークンに対して検証し、それらが GitHub から送信され、改ざんされていないことを確認します。\n\n## シークレット トークンの作成\n\nシークレット トークンを使用して新しい Webhook を作成することも、既存の Webhook にシークレット トークンを追加することもできます。 シークレット トークンを作成する際は、エントロピーの高いランダムな文字列を選択してください。\n\n* \"シークレット トークンを使用して新しい Webhook を作成する\" には、「*AUTOTITLE*」を参照してください。[](/ja/webhooks/using-webhooks/creating-webhooks)\n* \\_既存の Webhook にシークレット トークンを追加する\\_には、Webhook の設定を編集します。 \\[シークレット] に、`secret` キーとして使用する文字列を入力します。 詳しくは、「[webhookの編集](/ja/webhooks/using-webhooks/editing-webhooks)」をご覧ください。\n\n## シークレット トークンを安全に格納する\n\nシークレット トークンを作成したら、サーバーがアクセスできる安全な場所に格納する必要があります。 トークンをアプリケーションにハードコーディングしたり、トークンをリポジトリにプッシュしたりしないでください。 コードで認証資格情報を安全に使用する方法の詳細については、「[API 資格情報をセキュリティで保護する](/ja/rest/authentication/keeping-your-api-credentials-secure#use-authentication-credentials-securely-in-your-code)」を参照してください。\n\n## Webhook 配信を検証する\n\nGitHub は、シークレット トークンを使用して、各ペイロードとともにあなたに送信されるハッシュ署名を作成します。 ハッシュ署名は、各配信の `X-Hub-Signature-256` ヘッダーの値として表示されます。 詳しくは、「[Webhook のイベントとペイロード](/ja/webhooks/webhook-events-and-payloads#delivery-headers)」をご覧ください。\n\nWebhook 配信を処理するコードでは、シークレット トークンを使用してハッシュを計算する必要があります。 次に、送信 GitHub ハッシュを、計算した予想されるハッシュと比較し、それらが一致していることを確認します。 さまざまなプログラミング言語でハッシュを検証する方法を示す例については、「[例](#examples)」を参照してください。\n\nWebhook ペイロードを検証する際には、いくつかの重要な点に留意する必要があります。\n\n* GitHub は、HMAC の 16 進ダイジェストを使用してハッシュを計算します。\n* ハッシュ署名は常に、`sha256=` から始まります。\n* ハッシュ署名は、Webhook のシークレット トークンとペイロードの内容を使用して生成されます。\n* 言語とサーバーの実装で文字エンコーディングが指定されている場合は、ペイロードをUTF-8として扱うようにしてください。 Webhook ペイロードには Unicode 文字を含めることができます。\n* プレーン `==` 演算子は使用しないでください。 代わりに、「一定時間」の文字列比較を行う [`secure_compare`](https://www.rubydoc.info/gems/rack/Rack%2FUtils:secure_compare) や [`crypto.timingSafeEqual`](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b) などのメソッドを使用して、通常の等価演算子に対する特定のタイミングでの攻撃や、JIT 最適化言語における通常のループを緩和することを検討してください。\n\n### Webhook ペイロード検証のテスト\n\n次の `secret` と `payload` 値を使用して、実装が正しいことを確認できます。\n\n* `secret`: `It's a Secret to Everybody`\n* `payload`: `Hello, World!`\n\n実装が正しければ、生成するシグネチャは次のシグネチャ値と一致しています。\n\n* 署名： `757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17` <!-- markdownlint-disable-line GHD034 -->\n* X-Hub-Signature-256: `sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17`\n\n### 例一覧\n\n選択したプログラミング言語を使用して、コードに HMAC 検証を実装できます。 次に、実装がさまざまなプログラミング言語でどのように表示されるかを示す例をいくつか示します。\n\n#### Ruby の例\n\nたとえば、次のような `verify_signature` 関数を定義できます。\n\n```ruby\ndef verify_signature(payload_body)\n  signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)\n  return halt 500, \"Signatures didn't match!\" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256'])\nend\n```\n\nその後、Webhook ペイロードを受信したらそれを呼び出すことができます。\n\n```ruby\npost '/payload' do\n  request.body.rewind\n  payload_body = request.body.read\n  verify_signature(payload_body)\n  push = JSON.parse(payload_body)\n  \"I got some JSON: #{push.inspect}\"\nend\n```\n\n#### Pythonの例\n\nたとえば、次のような `verify_signature` 関数を定義し、Webhook ペイロードを受信したらそれを呼び出すことができます。\n\n```python\nimport hashlib\nimport hmac\ndef verify_signature(payload_body, secret_token, signature_header):\n    \"\"\"Verify that the payload was sent from GitHub by validating SHA256.\n\n    Raise and return 403 if not authorized.\n\n    Args:\n        payload_body: original request body to verify (request.body())\n        secret_token: GitHub app webhook token (WEBHOOK_SECRET)\n        signature_header: header received from GitHub (x-hub-signature-256)\n    \"\"\"\n    if not signature_header:\n        raise HTTPException(status_code=403, detail=\"x-hub-signature-256 header is missing!\")\n    hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)\n    expected_signature = \"sha256=\" + hash_object.hexdigest()\n    if not hmac.compare_digest(expected_signature, signature_header):\n        raise HTTPException(status_code=403, detail=\"Request signatures didn't match!\")\n```\n\n#### JavaScript の例\n\nたとえば、次のような `verifySignature` 関数を定義し、Webhook ペイロード受信時に呼び出すことができます。\n\n```javascript\nlet encoder = new TextEncoder();\n\nasync function verifySignature(secret, header, payload) {\n    let parts = header.split(\"=\");\n    let sigHex = parts[1];\n\n    let algorithm = { name: \"HMAC\", hash: { name: 'SHA-256' } };\n\n    let keyBytes = encoder.encode(secret);\n    let extractable = false;\n    let key = await crypto.subtle.importKey(\n        \"raw\",\n        keyBytes,\n        algorithm,\n        extractable,\n        [ \"sign\", \"verify\" ],\n    );\n\n    let sigBytes = hexToBytes(sigHex);\n    let dataBytes = encoder.encode(payload);\n    let equal = await crypto.subtle.verify(\n        algorithm.name,\n        key,\n        sigBytes,\n        dataBytes,\n    );\n\n    return equal;\n}\n\nfunction hexToBytes(hex) {\n    let len = hex.length / 2;\n    let bytes = new Uint8Array(len);\n\n    let index = 0;\n    for (let i = 0; i < hex.length; i += 2) {\n        let c = hex.slice(i, i + 2);\n        let b = parseInt(c, 16);\n        bytes[index] = b;\n        index += 1;\n    }\n\n    return bytes;\n}\n```\n\n#### TypeScript の例\n\nたとえば、次のような `verify_signature` 関数を定義し、Webhook ペイロードを受信したらそれを呼び出すことができます。\n\n```javascript copy\nimport { Webhooks } from \"@octokit/webhooks\";\n\nconst webhooks = new Webhooks({\n  secret: process.env.WEBHOOK_SECRET,\n});\n\nconst handleWebhook = async (req, res) => {\n  const signature = req.headers[\"x-hub-signature-256\"];\n  const body = await req.text();\n\n  if (!(await webhooks.verify(body, signature))) {\n    res.status(401).send(\"Unauthorized\");\n    return;\n  }\n\n  // The rest of your logic here\n};\n```\n\n## トラブルシューティング\n\nペイロードが GitHub から確実に取得されているが、署名の検証が失敗する場合:\n\n* Webhook のシークレットが構成されていることを確認します。 Webhook のシークレットを構成していない場合、`X-Hub-Signature-256` ヘッダーは存在しません。 Webhook シークレットの設定の詳細については、「[webhookの編集](/ja/webhooks/using-webhooks/editing-webhooks)」を参照してください。\n* 正しいヘッダーを使用していることを確認します。 GitHub では、HMAC-SHA256 アルゴリズムを使用する `X-Hub-Signature-256` ヘッダーを使用することをお勧めします。 `X-Hub-Signature` ヘッダーはHMAC-SHA1 アルゴリズムを使用し、従来の目的でのみ含まれています。\n* 正しいアルゴリズムを使用していることを確認します。 `X-Hub-Signature-256` ヘッダーを使用している場合は、HMAC-SHA256 アルゴリズムを使用する必要があります。\n* 正しい webhook シークレットを使用していることを確認します。 Webhook シークレットの値がわからない場合は、Webhook のシークレットを更新できます。 詳しくは、「[webhookの編集](/ja/webhooks/using-webhooks/editing-webhooks)」をご覧ください。\n* 検証の前にペイロードとヘッダーが変更されていないことを確認します。 たとえば、プロキシまたは負荷バランサーを使用する場合は、プロキシまたは負荷バランサーがペイロードまたはヘッダーを変更していないことを確認します。\n* 言語とサーバーの実装で文字エンコーディングが指定されている場合は、ペイロードをUTF-8として扱うようにしてください。 Webhook ペイロードには Unicode 文字を含めることができます。\n\n## 参考資料\n\n* [webhookの配信処理](/ja/webhooks/using-webhooks/handling-webhook-deliveries)\n* [Webhook の使用に関するベスト プラクティス](/ja/webhooks/using-webhooks/best-practices-for-using-webhooks)"}