Skip to main content
ドキュメントへの更新が頻繁に発行されており、このページの翻訳はまだ行われている場合があります。 最新の情報については、「英語のドキュメント」を参照してください。

Webhook のセキュリティ保護

セキュリティ上の理由から、サーバーが想定されているる GitHub リクエストのみを受信していることを確認する必要があります。

ペイロードを受信するようにサーバーが設定されると、設定したエンドポイントに送信されたペイロードがリッスンされます。 セキュリティ上の理由から、GitHub からのリクエストに制限することをお勧めします。 これを行うにはいくつかの方法があります。たとえば、GitHub の IP アドレスからのリクエストを許可することですが、はるかに簡単な方法は、シークレットトークンを設定して情報を検証することです。

REST API を利用し、リポジトリ、組織、アプリ Webhook を管理できます。 Webhook の Webhook 配信を一覧表示したり、Webhook の個別の配信を取得して再配信したりできます。Webhook は、外部のアプリまたはサービスに統合できます。 REST API を使用して、Webhook の構成を変更することもできます。 たとえば、ペイロードURL、コンテントタイプ、SSLの検証、シークレットを変更できます。 詳細については、次を参照してください。

シークレットトークンを設定する

シークレットトークンは、GitHub とサーバーの 2 か所に設定する必要があります。

GitHub にトークンを設定するには:

  1. Webhook を設定しているリポジトリに移動します。

  2. リポジトリ名の下にある [設定] をクリックします。 [設定] タブが表示されない場合は、 [] ドロップダウン メニューを選び、 [設定] をクリックします。

    タブを示すリポジトリ ヘッダーのスクリーンショット。 [設定] タブが濃いオレンジ色の枠線で強調表示されています。

  3. 左側のサイドバーで、 [ Webhooks] をクリックします。

  4. Webhook の横にある [編集] をクリックします。

  5. "シークレット" フィールドに、エントロピが高いランダムな文字列を入力します。 たとえば、ターミナルで ruby -rsecurerandom -e 'puts SecureRandom.hex(20)' を含む文字列を生成できます。

  6. [webhook の更新] をクリックします。

次に、このトークンを保存する環境変数をサーバーに設定します。 通常、これは実行と同じくらい簡単です。

$ export SECRET_TOKEN=YOUR-TOKEN

トークンをアプリにハードコーディングしないでください

GitHub からのペイロードを検証する

シークレットトークンが設定されると、GitHub Enterprise Server はそれを使用して各ペイロードでハッシュ署名を作成します。 このハッシュ署名は、x-hub-signature-256 として各要求のヘッダーに含まれています。

注: 下位互換性のために、SHA-1 ハッシュ関数を使用して生成される x-hub-signature ヘッダーも含まれています。 可能であれば、セキュリティを強化するために x-hub-signature-256 ヘッダーを使用することをお勧めします。 次の例では、x-hub-signature-256 ヘッダーを実際に使ってみます。

SECRET_TOKEN を使ってハッシュを計算し、結果が GitHub Enterprise Server のハッシュと一致することを確認する必要があります。 GitHub Enterprise Server は、HMAC 16 進ダイジェストを使ってハッシュを計算します。

注: Webhook ペイロードには Unicode 文字を含めることができます。 言語とサーバーの実装で文字エンコーディングが指定されている場合は、ペイロードをUTF-8として扱うようにしてください。

言語とサーバーの実装は、以下の例とは異なる場合があります。 ただし、次のようないくつかの非常に重要な事項があります。

  • どの実装を使用する場合でも、ハッシュ署名は sha256= で始まり、シークレット トークンのキーとペイロード本文を使用します。

  • プレーン == 演算子の使用はお勧めしませんsecure_compare のようなメソッドは、"定数時間" の文字列比較を実行します。これは、通常の等式演算子に対する特定のタイミング攻撃を軽減するのに役立ちます。

Ruby の例

たとえば、次のような verify_signature 関数を定義できます。

def verify_signature(payload_body)
  signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)
  return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256'])
end

その後、Webhook ペイロードを受信したらそれを呼び出すことができます。

post '/payload' do
  request.body.rewind
  payload_body = request.body.read
  verify_signature(payload_body)
  push = JSON.parse(payload_body)
  "I got some JSON: #{push.inspect}"
end

Python の例

たとえば、次のような verify_signature 関数を定義し、Webhook ペイロードを受信したらそれを呼び出すことができます。

import hashlib
import hmac
def verify_signature(payload_body, secret_token, signature_header):
    """Verify that the payload was sent from GitHub by validating SHA256.
    
    Raise and return 403 if not authorized.
    
    Args:
        payload_body: original request body to verify (request.body())
        secret_token: GitHub app webhook token (WEBHOOK_SECRET)
        signature_header: header received from GitHub (x-hub-signature-256)
    """
    if not signature_header:
        raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!")
    hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
    expected_signature = "sha256=" + hash_object.hexdigest()
    if not hmac.compare_digest(expected_signature, signature_header):
        raise HTTPException(status_code=403, detail="Request signatures didn't match!")