GitHubのWebhook送信元IPに3範囲追加|許可リストの点検手順と、署名で送り主を確かめる方法

GitHubが10月29日、Webhookの送信元IPに3つの範囲を追加したと告知

GitHubは2026年10月29日、github.comのWebhookを送り出すIPアドレスの範囲を3つ追加したと公式の変更履歴(Changelog)で告知しました。追加されたのは 9.234.98.160/28、9.234.98.176/28、9.234.106.48/28 です。GitHub Enterprise Cloudを含む、github.comのすべてのプランのWebhookが対象です。

Webhookはリポジトリへのpushやプルリクエストの作成といった出来事を、GitHubが外部のサーバーへ知らせる仕組みです。自動デプロイ、CI、チャットへの通知などの受け口として使われています。受け口のサーバーを守るために「GitHubのIPアドレスから来た通信だけを受け付ける」設定(IPアドレスの許可リスト)を使っている場合、新しい範囲から届いたWebhookは、リストに足りなければ拒否されます。

この記事では、追加された範囲と、GitHubがIPアドレスの一覧を公開している「Meta API」での扱いを確かめたうえで、自分の許可リストに足りない範囲があるかを調べる手順と、IPアドレスとは別にWebhookの送り主を確かめる署名の確認をまとめます。手順はMacのターミナルで実行して確かめました。

この変更の要点

IPアドレスでWebhookの受信を絞っているなら、3つの範囲を許可リストへ

追加された範囲

IPv4の3範囲、計48アドレス

9.234.98.160/28、9.234.98.176/28、9.234.106.48/28 です。

対応が必要な人

送信元IPを手で書いたリストで絞っている人

Meta APIから一覧を作り直している場合は、10月8日17時(協定世界時)より後に取り直した一覧なら入っています。

確かめ方

GitHubが公開するIPの一覧と比べる

一覧(Meta APIの「hooks」)と許可リストを、curlとPythonで照らし合わせます。

合わせて確認

署名でも送り主を確かめる

X-Hub-Signature-256 の確認は、IPアドレスが変わっても使えます。

追加された3つのIP範囲は、10月上旬からMeta APIの「hooks」に載っていた

GitHubは自社のサービスが使うIPアドレスの一覧を Meta API(https://api.github.com/meta)で公開しています。一覧は用途ごとに分かれていて、Webhookの送信元は「hooks」という項目です。10月31日に取得したhooksには、IPv4が7つ、IPv6が2つ、合わせて9つの範囲が並んでいました。

Webサイトの保存サービスInternet Archive(Wayback Machine)に残るMeta APIの保存版と比べると、3つの範囲が加わった時期も分かります。9月7日から10月6日までの保存版7点では、hooksは6つでした。10月8日以降の保存版3点では、今回の3つを含む9つになっています。Changelogでの告知は10月29日なので、Meta APIから一覧を取り直している人のリストには、告知より3週間ほど前から入っていたことになります。

10月6日までの保存版(6範囲)

以前からある範囲

192.30.252.0/22
185.199.108.0/22
140.82.112.0/20
143.55.64.0/20
2a0a:a440::/29
2606:50c0::/32

10月8日以降の保存版と現在(9範囲)

左の6範囲に、次の3つが加わった

9.234.98.160/28
9.234.98.176/28
9.234.106.48/28

/28は16アドレス分の範囲です。3つで48アドレスになります。

出典: Meta API(2026年10月31日に取得)と、Wayback Machineに残るMeta APIの保存版(9月7日〜10月23日の10点、協定世界時)。10月6日6時42分の版に3範囲はなく、10月8日16時58分の版にはありました。

新しい3つの範囲はhooksのほかに、GitHub Actionsの実行環境の項目「actions」にある 9.234.0.0/17 にも含まれます。actionsの範囲を含め、Meta APIに載る範囲をまとめて許可している環境では、以前から通る状態でした。ただしactionsの範囲はGitHubが用意する実行環境を多くの利用者が共有して使う範囲です。GitHubの説明どおり、ほかの利用者のワークフローからの通信もこの範囲から来るため、Webhookの受け口で許可する範囲にはhooksを使います。

対応が必要なのは、Webhookの送信元IPを固定のリストで絞っているサーバー

影響があるかどうかは、Webhookの受け口でIPアドレスによる制限をかけているかと、その一覧の作り方で決まります。次の順に確かめると、自分の環境で何をすればよいかが分かります。

Webhookの受け口で確かめること

質問1

ファイアウォール、リバースプロキシ、クラウドのアクセス制御などで、Webhookの受信をGitHubのIPアドレスに絞っている?

絞っていない → 今回の追加で設定を変える必要はありません。下の「IPアドレスに加えて、署名でGitHubからの送信だと確かめる」節を確認してください。
絞っている → 質問2へ

質問2

許可リストは、Meta APIから自動で作り直している?

自動で作り直している → 10月8日17時(協定世界時)より後に取り直した一覧なら、3範囲は入っています。次の節の手順で、念のため確かめます。
手で書いて固定している → 3範囲を追加します。

追加したあと

拒否されていたWebhookは、GitHubの「Recent deliveries」で失敗として残ります。過去3日分までは「Redeliver」で送り直せます。

許可リストで拒否されたWebhookは、受け口のアプリまで届きません。デプロイが動かない、通知が来ないといった形で気づくことが多く、GitHub側では「Recent deliveries」に失敗として記録されます。GitHubは失敗したWebhookを自動では送り直しません。GitHubの説明どおり、受け取れなかった分は手で、または送り直しのスクリプトで再送します。

1

リポジトリの「Settings」を開く

2

左の「Webhooks」から対象のURLを選ぶ

3

「Recent deliveries」で失敗した送信を探す

4

送信の識別子(GUID)を開き「Redeliver」を押す

出典: GitHub Docs「Redelivering webhooks」。送り直せるのは過去3日分で、リポジトリの管理者権限が必要です。Organizationに設定したWebhookはOrganizationのオーナーが、GitHub AppのWebhookはAppのオーナーか管理者が、それぞれの設定画面の「Recent deliveries」から送り直します。

Webhookの許可リストに足りない範囲を、Meta APIと比べて確かめる

まず、いまのWebhookの送信元を一覧で表示します。curlとjqを使うコマンドで、手元のMac(macOS 27.0.1)では両方とも最初から入っていました。ターミナルに貼り付けて実行してください。jqがない環境では、次に紹介するPythonのスクリプトだけでも確かめられます。Macで初めて python3 を実行すると、コマンドライン・デベロッパ・ツールのインストールを求められることがあります。

curl -s https://api.github.com/meta | jq -r '.hooks[]'
9.234.106.48/28
9.234.98.160/28
9.234.98.176/28
192.30.252.0/22
185.199.108.0/22
140.82.112.0/20
143.55.64.0/20
2a0a:a440::/29
2606:50c0::/32

許可リストに範囲が少ないうちは、この出力と目で見比べれば足ります。リストに広い範囲(たとえば 9.234.0.0/16)を書いている場合は、文字列が一致しなくても中に含まれていることがあります。そこで、範囲どうしの包含関係まで調べる短いPythonスクリプトを用意しました。許可リストの隣り合う範囲をまとめたうえで、hooksの各範囲がどれか1行の範囲に丸ごと含まれるかを判定します。/28 を2つの /29 に分けて書いたリストも、まとめてから比べるので含まれると判定されます。Pythonの標準ライブラリだけで動きます。

次の内容を check_hooks.py という名前で保存し、許可リストを1行に1範囲ずつ書いた allowlist.txt を同じフォルダに置きます。

import ipaddress
import json
import sys
import urllib.request

META_URL = "https://api.github.com/meta"


def load_hooks(source):
    if source.startswith("https://"):
        with urllib.request.urlopen(source, timeout=10) as res:
            meta = json.load(res)
    else:
        with open(source, encoding="utf-8") as f:
            meta = json.load(f)
    return [ipaddress.ip_network(x) for x in meta["hooks"]]


def load_allowlist(path):
    nets = []
    with open(path, encoding="utf-8") as f:
        for line in f:
            line = line.split("#", 1)[0].strip()
            if line:
                nets.append(ipaddress.ip_network(line, strict=False))
    return nets


def merge(nets):
    v4 = ipaddress.collapse_addresses(n for n in nets if n.version == 4)
    v6 = ipaddress.collapse_addresses(n for n in nets if n.version == 6)
    return list(v4) + list(v6)


def covered(net, allow):
    return any(
        net.version == a.version and net.subnet_of(a) for a in allow
    )


def main():
    if len(sys.argv) < 2:
        print("usage: python3 check_hooks.py allowlist.txt [meta.json]")
        return 2
    allow = merge(load_allowlist(sys.argv[1]))
    hooks = load_hooks(sys.argv[2] if len(sys.argv) > 2 else META_URL)
    missing = [str(n) for n in hooks if not covered(n, allow)]
    print(f"GitHubのWebhook送信元: {len(hooks)}件")
    print(f"許可リストに含まれる: {len(hooks) - len(missing)}件")
    if missing:
        print("足りない範囲:")
        for m in missing:
            print(f"  {m}")
        return 1
    print("足りない範囲はありません")
    return 0


if __name__ == "__main__":
    sys.exit(main())

ターミナルでそのフォルダへ移動し、次を実行します。

python3 check_hooks.py allowlist.txt

10月6日までの6範囲だけを書いた許可リストで実行すると、今回の3範囲が「足りない範囲」として表示され、終了コードは1になりました。Meta APIから作り直したリスト、9.234.0.0/16 を含む広いリスト、3範囲をそれぞれ2つの /29 に分けて書いたリストでは、足りない範囲は0件でした。

要追加:10月6日までの6範囲のリスト

GitHubのWebhook送信元: 9件
許可リストに含まれる: 6件
足りない範囲:
  9.234.106.48/28
  9.234.98.160/28
  9.234.98.176/28

対応済み:Meta APIから作ったリスト

GitHubのWebhook送信元: 9件
許可リストに含まれる: 9件
足りない範囲はありません

2026年10月31日、macOS 27.0.1のPython 3.9.6で実行した出力。

足りない範囲が出たら、使っている仕組み(ファイアウォール、nginxなどのリバースプロキシ、クラウドのセキュリティグループなど)の許可リストに追加します。GitHubのWebhookのベストプラクティスは、IPアドレスで絞る場合はMeta APIから最新の一覧を取り、定期的に更新するよう勧めています。上のスクリプトは終了コードで結果を返すので、定期実行やCIに組み込めば、次に範囲が増えたときにも気づけます。

IPアドレスに加えて、署名でGitHubからの送信だと確かめる

GitHubは自社のIPアドレスについて説明するページで、IPアドレスによるアクセス許可を勧めておらず、使う場合はMeta APIを定期的に確認するよう求めています。範囲は今回のように増えるため、固定の許可リストは更新が遅れると、新しい範囲から届くWebhookを拒否します。

そこで合わせて使いたいのが、Webhookの署名です。Webhookに「Secret」(合言葉にあたる文字列)を設定すると、GitHubは送る内容とSecretから計算した値を X-Hub-Signature-256 というヘッダーに付けます。受け口で同じ計算をして一致すれば、Secretを知るGitHubから届いた、途中で書き換えられていない内容だと確かめられます。この確認は送信元のIPアドレスが変わっても同じように使えます。

Pythonでは、GitHubの説明にあるPythonの例をもとにした次の関数で確かめられます。署名は送られてきた本文そのものから計算されるので、受け取った本文はJSONとして読み込む前のバイト列(Pythonの bytes)のまま渡します。比較には、GitHubが勧めるとおり == の代わりに hmac.compare_digest を使い、両方をバイト列にそろえて比べます。署名ヘッダーに日本語などASCII以外の文字が混ざった不正な送信も、エラーで止まらずに拒否できます。

import hashlib
import hmac


def is_valid_github_signature(raw_body, secret, header):
    if not header:
        return False
    digest = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    expected = "sha256=" + digest
    return hmac.compare_digest(expected.encode("utf-8"), header.encode("utf-8"))

GitHubの説明に載っている例の値(Secretが「It's a Secret to Everybody」、本文が「Hello, World!」)で試すと、記載どおりの署名と一致しました。本文を1文字変えたもの、署名ヘッダーがないもの、署名ヘッダーに日本語が入ったものは拒否されます。

受け付ける

GitHubの例と同じ本文と署名

拒否する

本文を1文字変えた(「!」を「?」に)

拒否する

署名ヘッダーが付いていない

拒否する

署名ヘッダーに日本語が入っている

2026年10月31日、macOS 27.0.1のPython 3.9.6で上の関数を実行した結果。期待する署名は sha256=757107ea…3e17(GitHub Docsの例)。

Secretは推測されにくい長い文字列にし、受け口のサーバーだけが読める場所に置きます。まだSecretを設定していないWebhookは、リポジトリの「Settings」→「Webhooks」で対象のURLを開き、「Secret」欄に入れて保存すると、以後の送信に署名が付きます。

署名を確かめるようにしても、IPアドレスの許可リストで拒否された通信は受け口に届きません。IPアドレスで絞り続ける場合は、許可リストをMeta APIから定期的に作り直し、署名は送り主の確認として併せて使います。

Webhookの許可リストを点検し、署名の確認も入れておく

GitHubがWebhookの送信元に加えた3つの範囲は、Meta APIの「hooks」には10月上旬から載っていました。送信元IPで受信を絞っているサーバーは、curl -s https://api.github.com/meta | jq -r '.hooks[]' の出力か上のスクリプトで許可リストを点検し、足りない範囲を追加してください。拒否されたWebhookは、過去3日分なら「Recent deliveries」から送り直せます。IPアドレスで絞り続けるなら、許可リストはMeta APIから定期的に作り直します。あわせて X-Hub-Signature-256 の署名を確かめるようにしておくと、IPアドレスとは別の方法で、GitHubから届いた書き換えられていない内容かを確かめられます。