Webhook を管理する

GDPR 規制に従ってユーザーのプライバシーを向上させるため、Bitbucket やその他の Atlassian Cloud 製品では個人データへのアクセスの管理方法を統一するために製品 API の更新を進めています。これらの API の変更は、アトラシアン製品で今後予定されている改善である、データの表示およびアクセスを許可するユーザーのより詳細な制御をサポートするために必要です。

username エンドポイントと username フィールドの提供は 2019 年 4 月 12 日に廃止する予定です。アトラシアンではこれらを置き換えるため、すぐに使用できる複数の新しいデータ ポイントを導入します。詳細は、API の廃止のお知らせを参照してください。

Webhook を使うことで、Bitbucket Cloud で特定のイベントが発生したときにセルフホスト型のサーバー (または外部サービス) にリクエストを送信するよう、Bitbucket Cloud を構成できます。webhook の構成要素は次のとおりです。

  • 適用対象 - イベントを生成するリソース。現在、このリソースは Webhook を作成するリポジトリです。

  • 1 つ以上のイベント - デフォルトのイベントはリポジトリ プッシュですが、Webhook をトリガーできるイベントを複数選択できます。

  • URL – 設定した条件に一致するイベントが発生した場合に、Bitbucket がイベント ペイロードを送信するエンドポイント。

Webhook を機能させるための作業として、Webhook の作成と Webhook のトリガーの 2 段階の手順があります。イベント用の Webhook を作成すると、イベントが発生するたびに、Bitbucket はイベントについてのペイロード リクエストを指定した URL に送信します。そのため、Webhook は通知システムのようなものだと考えることができます。

Webhook に問題がある場合は、「Webhook のトラブルシューティング」を参照してください。

Forge を使用して Bitbucket Cloud 用アプリを開発したい場合

詳細やアプリ構築に役立つ情報については、「Bitbucket Cloud 用のアプリの作成」を参照してください。

Webhook を使用するタイミング

Webhooks を使用して Bitbucket Cloud とアプリケーションを連携できます。次のような例があります:

  • ユーザーがリポジトリにコミットをプッシュするたびに CI サーバーにビルドを開始するよう通知する

  • ユーザーがコミットをプッシュしたり、プルリクエストを作成するたびに、アプリケーションで通知を表示する

Webhook のメリット

Webhook を使用しない場合、Bitbucket Cloud でのイベント発生を確認するには、API をポーリングする必要があります。ただし、API のポーリングは利便性が低く非効率な場合があり、エラーも発生しがちです。携帯電話での SMS メッセージの仕組みを考えてみてください。電話が通知を送信するため、5 分ごとにメッセージ アプリを確認して新着情報があるかどうかを確認する必要はありません。同様に、Webhook は通知のように機能するため、API で 1 分おきに同じアクティビティを確認する必要はありません。

Webhook を作成する

Bitbucket Cloud または API から Webhook を作成できます。次の手順を使用して、Bitbucket のリポジトリで Webhook を作成します。リポジトリの管理者のみがリポジトリで Webhook を作成できます。

リポジトリあたり最大 50 件の Webhook を作成できます。

  1. Bitbucket で Webhook を追加したいリポジトリを開きます。

  2. 左側のサイドバーで [リポジトリ設定] をクリックします。

  3. [設定] ページで、左側のサイドバーの [Webhook] を選択します。

  4. [Webhook の追加] ボタンをクリックしてリポジトリに Webhook を作成します。

  5. [新しい Webhook の追加] ページで、短い説明を含むタイトルを入力します。

  6. アプリケーションまたはサーバーへの URL を入力します。

  7. (推奨) 受信した Webhook ペイロードを認証できるように、シークレットを入力します。詳細については、"Webhook を保護する" のセクションを参照してください。

  8. (オプション) 保存後に Webhook をアクティブにしたくない場合は、[アクティブ] からチェックマークを外します。

  9. (オプション) 自己署名証明書を使用しているために証明書の検証を無効化したい場合は、[証明書の検証をスキップ] を選択します。

    注: 自己署名証明書は本質的に安全でないため、証明書の検証を無効化しないことをお勧めします。自己署名証明書の使用の詳細については、次のセクションをお読みください。

  10. 必要に応じて、[トリガー] フィールドを更新します。
    既定の Webhook トリガーは、[リポジトリ プッシュ] フィールドで示される、リポジトリのプッシュです。Webhook トリガーを追加したり、別の操作でトリガーさせたい場合は、[トリガーの完全な一覧から選択] をクリックします。Webhook をトリガーできるすべてのイベント タイプの一覧が表示されます。

  11. Webhook に必要な情報をすべて入力した後、[保存] をクリックします。

API を使用して Webhook を作成するには、Bitbucket が期待する HTTP リクエストの形式と、Bitbucket がサーバーに返す HTTP 応答の形式を知っておく必要があります。これらのリクエストや応答の説明と例について、参考ドキュメントを参照してください。

また、許可リストに追加する Bitbucket Cloud IP アドレスを確認しておく必要がある場合があります。

自己署名証明書を使用する場合

Webhook を作成する際、Bitbucket は証明書の検証をスキップするオプションを提供します。このオプションでは、イベント ペイロード リクエストを Webhook URL に送信する際に、証明書の検証を無効化できます。このオプションは、自己署名の SSL 証明書を使用している HTTPS エンドポイントで Webhook を受信する場合に使用できます。ただし、自己署名証明書は本質的に安全でないため、以下の場合を除いては証明書の検証を無効化しないことをお勧めします。

  • 公共の認証局が署名した有効な SSL 証明書をインストールできない。また、

  • セキュリティを考慮する必要がない Webhook である

HTTPS エンドポイントを使用すると、送信データは対称キーを使用して暗号化され、対称キー自体は SSL 証明書の公開キーを使用して暗号化されます。ただし、自己署名証明書を使用している場合、証明書は信頼できる証明局の署名を受けていないため、証明書を信頼できるものとみなすことができません。その結果、自己署名証明書を使用して HTTPS エンドポイント経由でデータを送信する場合、意図するサーバーと通信しているかどうかを把握することができません。つまり、証明書が検証されていない場合、不正な第三者がサーバーになりすまして MITM (介入者) 攻撃を実行できてしまうことになります。

可能であれば、証明書の検証を無効化しないことをお勧めします。初回のキー交換の間に双方の身元を確実に確認できる唯一の方法は、信頼できる証明局の SSL 証明書を取得しておくことです。これにより、Bitbucket から送信されたデータが、他の人物ではなく自分の証明書の公開キーを使用して正しく暗号化されることを確認できます。安価な SSL 証明書のオプションについては次をご覧ください。

Webhook を保護する

Webhook 配信について

ペイロードを受信するように設定されたセルフホスト型のサーバーは、指定のエンドポイントに送信されるすべての配信をリッスンします。セキュリティ上の理由から、Bitbucket Cloud からの配信のみを処理する必要があります。

セルフホスト型のサーバーで Bitbucket Cloud からの配信のみを処理するようにするには、次のことを行う必要があります。

  1. Webhook のシークレット トークンを作成する。

  2. トークンをセルフホスト型のサーバーに安全に保管する。

  3. 受信した Webhook ペイロードをトークンと照合して、Bitbucket Cloud からのものであることを検証します。

シークレット トークンの作成

シークレット トークンを使用して新しい Webhook を作成することも、既存の Webhook にシークレット トークンを追加することもできます。シークレット トークンを作成する場合は、エントロピーの高いランダムなテキスト文字列を選択する必要があります。"Generate secret (シークレットを生成)" ボタンを使用して、シークレットを自動生成することもできます。 シークレットは必ず安全な場所に記録してください。Webhook を保存すると、シークレットの閲覧も取得もできなくなります。

シークレット トークンの更新

Webhook を編集することで、既存の Webhook のシークレット トークンを更新できます。ただし、このシークレットを使用しているインテグレーションはすべて、更新する必要があることに注意してください。

シークレット トークンの安全な保存

シークレット トークンを作成したら、サーバーからアクセスできる安全な場所に保存する必要があります。トークンをアプリケーションにハードコードしたり、リポジトリにプッシュしたりしないでください。

Webhook 配信の検証

Bitbucket Cloud はシークレット トークンを使用して、ペイロードごとに送信される HMAC 署名を作成します。ハッシュ署名は、X-Hub-Signature ヘッダーの値として、各配信に表示されます。ヘッダーの値は、WebSub で定義されている method=signature 形式にしてください。method は、WebSub にリストされているハッシュのいずれかです。signature は、method で指定されているハッシュを使用した本文の HMAC になります。現在、Bitbucket は、sha256 を使用して HMAC を送信します。これは将来変わる可能性があります。

Webhook の配信を処理するコードでは、シークレット トークンと method で指定されたハッシュを使用して、本文の HMAC を計算する必要があります。次に、Bitbucket Cloud から送信された HMAC と、計算で予想される HMAC を比較して、それらが一致することを確認します。さまざまなプログラミング言語でハッシュを検証する方法を示す例については、"" のセクションを参照してください。

Webhook ペイロードを検証する際に留意すべき重要な点がいくつかあります。

  • Bitbucket Cloud は HMAC の 16 進ダイジェストを使用してハッシュを計算します。

  • HMAC は、Webhook のシークレット トークン、ペイロードの内容、および method に記載されているハッシュ アルゴリズムを使用して生成されます。

  • ペイロードは HMAC 生成に 逐語的に 渡されることに注意してください。HMAC 署名を検証する場合、文字列として「生の」コード形式を使用する必要があります。そうしないと、署名が一致しません。HMAC を生成する前に本文に書式設定や美化を実行すると、異なる署名になります。

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

  • プレーン テキストの == 演算子は絶対に使用しないでください。代わりに、secure_comparecrypto.timingSafeEqual のような方法の使用を検討してください。これらの方法では、文字列の "定数時間" 比較を行うことで、通常の等価演算子に対する特定のタイミング攻撃や、JIT 最適化言語での標準ループを軽減するのに役立ちます。

Webhook ペイロード検証のテスト

次の secretpayload、および method の値を使用して、実装が正しいことを確認できます。

  • secret:It's a Secret to Everybody

  • payload:Hello World!

  • method:sha256

実装が正しければ、生成される署名が次の署名値と一致します。

  • 署名: a4771c39fbe90f317c7824e83ddef3caae9cb3d976c214ace1f2937e133263c9

  • X-Hub-Signature: sha256=a4771c39fbe90f317c7824e83ddef3caae9cb3d976c214ace1f2937e133263c9

任意のプログラミング言語を使用して、コードに HMAC 検証を実装できます。以下は、実装がいくつかのプログラミング言語でどのようになるかを示す例です。これらの例では、Bitbucket が sha256 ハッシュを使用して HMAC を送信していると想定していることに注意してください。Bitbucket は将来、HMAC に別のハッシュを使用するようになる可能性があります。その場合、これらのサンプル コードは正しく動作しなくなります。

Java の例

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HexFormat; public class Main { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { final String secret = "It's a Secret to Everybody"; final String payload = "Hello World!"; final String givenSignature = "sha256=a4771c39fbe90f317c7824e83ddef3caae9cb3d976c214ace1f2937e133263c9"; final SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); final Mac mac = Mac.getInstance("HmacSHA256"); mac.init(keySpec); final byte[] digest = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8)); final HexFormat hex = HexFormat.of(); final String calculatedSignature = "sha256=" + hex.formatHex(digest); if (!MessageDigest.isEqual(calculatedSignature.getBytes(), givenSignature.getBytes())) { System.out.println("Signatures do not match\nExpected signature:" + calculatedSignature + "\nActual: signature: " + givenSignature); } else { System.out.println("Signatures match"); } } }

Python の例

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import hashlib import hmac secret = "It's a Secret to Everybody" payload = "Hello World!" given_signature = "sha256=a4771c39fbe90f317c7824e83ddef3caae9cb3d976c214ace1f2937e133263c9" hash_object = hmac.new( secret.encode("utf-8"), msg=payload.encode("utf-8"), digestmod=hashlib.sha256, ) calculated_signature = "sha256=" + hash_object.hexdigest() if not hmac.compare_digest(calculated_signature, given_signature): print( "Signatures do not match\nExpected signature:" f" {calculated_signature}\nActual: signature: {given_signature}" ) else: print("Signatures match")

Node.js例

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const { createHmac, timingSafeEqual } = require('node:crypto'); const secret = "It's a Secret to Everybody" const payload = "{\"hello\":\"world\",\"webhook\":\"secret\"}"; const givenSignature = "sha256=c48e50b1d349b665dd7bf48bd243f22d5a22758c3f86714f0774aac3cab8fc5e" const hashObject = createHmac('sha256', secret).update(payload); const calculatedSignature = `sha256=${hashObject.digest('hex')}`; const signaturesMatch = timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(givenSignature)); if (!signaturesMatch) { console.log(`Signatures do not match\nExpected signature: ${calculatedSignature}\nActual: signature: ${givenSignature}`); } else { console.log("Signatures match"); }

Webhook をトリガーする

Webhook に関連付けられたイベントが発生すると、Bitbucket Cloud は、イベント ペイロードを含むリクエストを Webhook URL に送信します。

受信するペイロードが Bitbucket から送られたものかどうかをサーバーで確認したい場合、特定の IP アドレスを許可リストに追加する必要がある場合があります。詳しくは、「会社ファイアウォールを構成する際に、どの Bitbucket Cloud IP アドレスを使用する必要がありますか?」を参照してください。

次のイベント用に Webhook を作成できます。

各イベント ペイロードの説明と例については、イベントのドキュメントを参照してください。

さらにヘルプが必要ですか?

アトラシアン コミュニティをご利用ください。