1 ポイント 投稿者 GN⁺ 2 시간 전 | 1件のコメント | WhatsAppで共有
  • CDNなしで HTTPプロトコル/IP帯域/クライアント信号/TCP特性/TLSフィンガープリント/コンテンツ圧縮を組み合わせ、実装や設定が不十分なボットの大半をブロックする方法をまとめている
  • すべての方法は選択的に調整する必要があり、1〜3年分のアクセスログを先に分析しないと、VPNユーザー/検索エンジン/CDN/学校/図書館/特定言語のユーザーまで一緒にブロックしてしまう可能性がある
  • HTTP/1.1クライアントとデータセンターのAS/CIDR帯域をブロックすると多くのボットを排除できるが、GoogleBotをはじめとする検索エンジンや正常なデータセンター利用者も除外される可能性がある
  • Nginxのヘッダー検査とnftablesのTCPウィンドウサイズ/MSS/TTLフィルターは単純なクローラーやスキャナーを減らせるが、LTE/VPN/Windowsのような正常な環境で誤検知が発生する可能性がある
  • 長期的には JA4 TLSフィンガープリント検出とBrotli専用レスポンスを代替案として提示しているが、完全な遮断は保証せず、収益を生むサービスでは使わないよう警告している

ブロック範囲と適用の前提

  • ブロック対象を一部のボット/大半のボット/すべてのボットのどれにするかを先に決める必要がある
    • ここで扱う方法は、高度な自動化ツール全体ではなく、実装や設定が不十分なボットの大半を比較的単純にブロックすることに重点を置いている
    • 各方法ごとに、正常なユーザーと検索エンジンをブロックするリスクも併記している
  • 2026年7月26日にHacker Newsで共有された後、ブロック機能の大半をブログから別のデモサイトへ移し、読者が方法を読んだ後に自分でアクセスを試みるパズル形式に変更することにした
  • すべての設定は選択的に修正または省略でき、実際に適用する前には十分な調査とテストが必要
  • 正常なユーザー/組織内システム/依存している外部サービスがブロックされる可能性があるため、適用責任はすべて運用者にある
  • 収益を生む運用環境では使わないこと

方法1: HTTPプロトコルで区別する

  • 正常なユーザーをブロックするリスクは低く、一部の検索エンジンをブロックするリスクは中程度
  • 一般的なブラウザはHTTP/2.0を使う一方、多くのボットはHTTP/1.1を使うという違いを利用する
    • GoogleBotはHTTP/1.1を使うと見られ、この方法でブロックされる
    • BingとFacebookのクローラーはHTTP/2.0を使う
    • HTTP/1.1でリンクタイトルや短いプレビューを取得するサービスもブロックされる可能性がある
    • Opera Miniも対象から除外する
  • Nginxで $server_protocolHTTP/2.0 でなければ、別ページへリダイレクトするか、200403444 を返すように構成する
if ($server_protocol != HTTP/2.0) {  
    return 403 'Upgrade your client';  
}  
  • 444 を返すと、別途レスポンスを返さずに接続を切断できる
  • Google検索からの流入をブロックしたときに生じる損失より、得られる利点が大きいかどうかは各組織が自分で判断する必要がある

方法2: データセンターIP帯域をブロックする

  • 正常な家庭用/LTEユーザーをブロックするリスクは低いが、VPNユーザーは中程度で、データセンターで動作する検索エンジンはブロックリスクが高い
  • 直近1〜2年のアクセスログから、次の信号を組み合わせて疑わしいリクエストを探す
    • HTTPプロトコル
    • User-Agent
    • Accept-Language
    • Sec-Fetch-Mode
    • Accept
  • 疑わしいIPを BGP Tools または Hurricane Electric BGP Toolkit で調べ、所属ASと広告されているPrefixを確認する
  • 提供されている ネットワークブラックホール一覧 にはCDNや検索エンジンの帯域も含まれる可能性があるため、選別して適用する必要がある
  • 別リストに先立ち、3/810/811/825/826/838/841/860/861/8200/8224/3 帯域をすでにブラックホール化した構成を使う
  • ASのPrefixページをコピーした後、シェル関数でCIDRだけを抽出し、ソート/重複削除/マージする
    • sum_cidr.pl を使い、Perlモジュール Net::CIDR::Lite が必要
    • 生成された結果は確認後、/usr/local/etc/*.netset ファイルへ移動する
  • サーバー起動時に各CIDRをブラックホールルートとして追加する
for CflIP in $(grep -E ^[1-9] /usr/local/etc/_cloudflare.netset); do  
    /sbin/ip route add blackhole "${CflIP}" 2>/dev/null  
done  
  • 例ではCloudflare全体の帯域をブロックし、Workersなど経由のリクエストを受けないようにしている
  • ファイアウォールのipsetルールより Linuxのブラックホールルーティングの方がCPU使用量が少ない という理由で、ルーティング方式を選んでいる
  • 現在のサーバーが属するホスティング事業者の帯域もブロックできる
    • DNS/設定/内部サービスで同じアドレス空間を使っていないことが必要
    • 直接接続されたゲートウェイルートはブラックホールルートより優先される

方法3: 国/プロキシ/Tor/悪性IPをブロックする

  • FireHOL Blocklists リポジトリのリストをダウンロードし、必要な帯域をブラックホールルートとして追加する
  • リストファイルにはコメントが含まれるため、grep -Ev '^#' で除去してから処理する必要がある
  • 特に次のリストを推奨している
    • firehol_abusers_30d.netset
    • firehol_level2.netset
  • 大きなリストは起動スクリプトの実行時間が長くなる可能性がある
  • 実際のサーバーとファイアウォール構成で使った設定ファイルも提供している

方法4: HTTPクライアント信号を検査する

  • User-Agentやヘッダーは偽装できるが、速度を優先する単純なボットはそれらを適切に偽装しないことが多い、という前提を使う
  • CurlWget リクエストにはプレーンテキストを返し、BotGPTLLMSpider が含まれるリクエストには 410 Gone を返す
if ($http_user_agent ~* Curl)   { return 200 '\nGNU Terry Pratchett\n\n'; }  
if ($http_user_agent ~* Wget)   { return 200 '\nGNU Terry Pratchett\n\n'; }  
if ($http_user_agent ~* Bot)    { return 410 '1000101'; }  
if ($http_user_agent ~* GPT)    { return 410 '1000101'; }  
if ($http_user_agent ~* LLM)    { return 410 '1000101'; }  
if ($http_user_agent ~* Spider) { return 410 '1000101'; }  
  • 観測されたUser-Agentの一部文字列を1つの長い正規表現で検査し、クローラー/スキャナー/収集ツールをブロックする
    • Go-httpJavalibwwwokhttpurllibpythonnmapzgrabsemrushshodanrssscrapcrawlerheadlessgithubfacebookgooglebing などに該当する部分文字列が含まれる
  • 適用前に2〜3年分のUser-Agentを集計し、実際の正常なクライアントが正規表現に一致するか確認する必要がある
sort access-user-agents.txt | uniq -c | sort  
  • 一致したリクエストがHTTP/1.1なら、ボットである可能性が高いものとして扱うが、GoogleBotは例外として考慮する
  • HTTP/2.0リクエストであれば、BGPツールでIPの所属を追加確認する

Sec-Fetch-Mode

  • Sec-Fetch-Mode をアクセスログに追加し、値が corsno-corsnavigate のいずれかでなければブロックする
if ($http_sec_fetch_mode !~ (cors|no-cors|navigate)) {  
    return 410 '1000101';  
}  

Referer検査

  • 他サイトからコンテンツを埋め込んだりスキャンしたりするリクエストを防ぐため、Referer に特定の文字列があればブロックする
    • 管理者ページ/検索エンジン/ソーシャルネットワーク/暗号資産/成人向けコンテンツ/スキャナー/WordPress関連文字列などを検査する
  • Googleのルートページ https://www.google.com/ をRefererだと主張する特定のボット種別も別途ブロックする
    • 古いAndroidを装うリクエストで観測されたパターン

HTTPメソッド制限

  • 通常のブラウザリクエストに必要な GETPOST だけを許可し、他のメソッドはブロックする
if ($request_method !~ (^GET$|^POST)) {  
    return 410 '1000101';  
}  
  • 実際のアプリケーションで POST が必要なパスをさらに細かく制限したり、使わない場合は完全に除外したりできる

プロキシとブラウザらしさの検査

  • X-Forwarded-For ヘッダーがあればプロキシリクエストと判断してブロックする
    • 学校や図書館のような正常な共有プロキシ環境もブロックされる可能性がある
  • User-Agentに LinuxBSDMacintoshWindowsMozillaWhatsApp のいずれも含まれていなければ、ブラウザらしくないリクエストと判断する
  • Accept-Languageen または es がなければブロックするルールも使う
    • 一部のブラウザと、英語/スペイン語を使わない正常なユーザーをブロックする可能性が大きい
  • br または sy を含む言語設定を別途ブロックする任意ルールも提示している

センシティブなパスのスキャンをブロックする

  • 次のファイルまたはパスをリクエストした場合、自動スキャンと見なしてブロックする
    • .git
    • .yml
    • .db
    • .sql
    • .conf

方法5: nftablesでTCPスキャナーをブロックする

  • nftablesの raw テーブル PREROUTING チェーンでTCP SYNパケットの特性を検査する
  • 例のサーバーアドレス 172.238.221.88 を宛先として明示し、パケットロス時に発生しうる誤検知を減らす
  • 80/443ポートに入ってくるSYNパケットのうち、次の条件をブロックする
    • TCPウィンドウサイズが 12,288バイト未満
    • MSSが 1,220〜1,460の範囲外
  • 実際のクライアントはより大きなウィンドウサイズを使い、この範囲を外れたMSSは正常なクライアントである可能性が低い、という基準を使う
  • MSSを正確に 1460 に制限するとより厳格になるが、LTEやVPNユーザーの大半をブロックする可能性がある

TTLベースの任意制限

  • TCP SYNのTTLが 128 より大きい場合、LTEデバイスの大半をブロックできる
  • TTLが 64 より大きい場合、Windowsシステムの大半もブロックされる
  • デフォルトTTLの目安は次のように説明している
    • Linux/Mac/BSD: 64
    • Windows: 128
    • LTE: これよりさらに大きい値

コネクション追跡の除外

  • 80/443ポートを notrack に指定し、conntrackテーブルにWebトラフィックを入れない
  • この場合、filterテーブルでも送受信方向について ステートレスなルール を直接構成する必要がある
  • 例ではクライアントの送信元ポート 1000-65535 とサーバーの80/443ポート間のトラフィックを許可している

方法6: 成人向けコンテンツとロボットヘッダー

  • 成人向けコンテンツへのアクセス制限には RTA: Restricted To Adults ヘッダーを使う
  • RTA以外の年齢確認方式は、ユーザー追跡と収益化のためのものだと見ている
  • Nginxレスポンスに次のヘッダーを常に追加する
add_header Rating 'RTA-5042-1996-1400-1577-RTA' always;  
add_header adult 'porn, sex, politics, religion, philosophy' always;  
add_header X-Robots-Tag "none,noindex,nofollow,nosnippet,noai" always;  
  • 一般的なボットはこうしたヘッダーを無視する可能性があるが、検索エンジンや成人向けコンテンツを避けるよう設計されたボットには影響する可能性がある

方法7: TLSフィンガープリント検出

  • 前述の1〜6番の方法はいずれも粗いヒューリスティックであり、長期的には TLSフィンガープリント分析 の方が良い選択肢になりうる
  • ボットがTLSフィンガープリントを通常のブラウザと同じに変更しないという条件で、JA4を活用できる
  • まず Deploying JA4 のデプロイ方法を確認したうえで、FoxIO JA4 を適用するよう案内している

方法8: Brotli圧縮コンテンツだけを提供する

  • サイトコンテンツを事前にBrotliで圧縮し、Webサーバーが圧縮済みファイルだけを返すように構成する
  • 多くのボットがBrotli圧縮HTMLを解釈できない点を利用する
  • 適用後、複数のボットがページリンクをそれ以上たどらなくなり、HTMLを実際にはパースできていないことを確認した
  • Nginxでは静的Brotliファイルを常に提供するよう設定する
brotli_static always;  
  • HTMLファイルは次のように事前圧縮する
cat ./i.html | brotli --best -fncv > ./i.html.br  

方法9: スキャナーに自己識別させる

  • 脆弱性パスを繰り返し探索する初心者攻撃者やスキャナーを減らす別の方法は Help Attackers Self Report で扱っている

1件のコメント

 
GN⁺ 2 시간 전
Hacker Newsのコメント
  • 複数の公開Webサイトを運営し、他サイトをクロールしてツールに活用する立場としては、なぜ人々がボットをそこまで気にするのか気になる
    キャッシュ付きのWordPressでも最安のVPSで毎秒約 1,000リクエスト を処理できるし、きちんと作られた静的サイトならその10倍も可能そう。ブログをLambdaのようなもので配信しているのか、それとも強迫観念や脆弱性対策、クロールが実サービスに影響していた時代の習慣なのか気になる

    • 自分の場合は Forgejoインスタンス が問題。ブログはページ数が限られた静的ファイルなので大丈夫だが、Forgejoはボットが事実上無限にページを見つけられ、一部のページ生成時にはバックグラウンドでGitまで実行する動的サービスなので、小さなサーバーは簡単に過負荷になる
      リポジトリはオープンソースなので意図的に公開している。今は簡単なCookieチェックで防御しており、通過できるボットは少数だが、検索エンジン露出を犠牲にするトレードオフになっている
    • 共有ホスティング上の個人サイトが、絶え間ない AIボットのクロール によるCPU過剰使用で最近停止された。クロール自体より、ボットが多すぎて非効率に動くことが問題
    • ボット運営者が避けたり回避したりしにくい、JavaScriptのような共通特性を見つけるのを楽しむ実験。このブログはRAMディスク上の事前圧縮された静的コンテンツなので、毎秒数十万リクエストでも処理できそう
      フォーラム、画像掲示板、チャットサーバーなどに適用する方法を示そうとしているもので、すべての選択肢は調整または無効化できる。実運用前にはテストサーバーで検証すべきで、ただ笑って流しても構わない
    • 自宅では 40Gbit回線 とそれに見合うサーバーを用意できない。Google Cloud VPSが数台、nmapや各種Web脆弱性検査を回しながら分散サービス拒否攻撃をしてくるだけでも、低スペック機材の性能は簡単に落ちる
      DMZが塞がってローカルIMAPサーバーの確認に数秒余計にかかるのは大事ではないが、だからといってそれを好んだり、許し続ける理由にもならない
    • ボット対策より有益なことに使える時間を奪われるのが最大の問題
      週末に、10〜20年間運用していたviewvc(CVS・Subversion)とhgweb(Mercurial)のWebインターフェースを終了した。住宅用プロキシIPから1日 270万件、平均毎秒30件のリクエストが来て、古いuWSGI/CGIプログラムや同じサーバー上の他サイトに負荷を与え、トラフィックもVPS上限の月1TBに近づいていたため
      動的なVCS URLの組み合わせは数百万通りあり得るのでキャッシュ効果も不確かで、サーバーをさらに調整することに時間を使う価値もなく、結局は中央集権的インターネットへまた一歩近づく選択になった
  • “承認済み” のユーザーエージェント以外をすべて遮断すると、既存の ブラウザ独占 を助け、ディストピアを早めることになる。RMSが何十年も前から警告していたのもこういう問題
    問題なら、トラフィック量とリクエスト頻度を基準に止めるべき。自分もこのサイトにアクセスできないが、それに合わせて行動するつもりはないし、DRMのように意志の強い相手はいずれ突破する

    • その批判は受け入れられる。RMSとは何度か一緒になったことがあり、興味深く非常に聡明な人物で、この件で会っていたら延々と小言を聞かされた気がする
      ただし、どんなブラウザでも使えるという主張とは別に、その場しのぎで生成したコードだったり、悪意あるサイトに対して十分に検証されていないブラウザには特に注意すべき。リーダーアプリも、侵入テストの専門家による広範なサードパーティコードレビューを受けていなければ、悪意あるサーバー に脆弱かもしれない
    • 挙動を基準に判断することもできる。go-awayは画像とCSSを読み込むか、メタリフレッシュのリダイレクトをたどるかを検査し、Anubisは数秒間 JavaScript実行 が可能か確認する
    • ユーザーエージェント文字列自体が概して有害。新しいブラウザなら、単に Chromeのユーザーエージェント をコピーするほうがよい
  • 169.254.169.254 を指す偽の cpanelサブドメイン を追加すると、初心者攻撃者に自分のホスティング事業者をポートスキャンさせることになり、検知または遮断され得るという発想が気に入っている

    • 最初に実験したときは、何も起きないと思っていた。数日もしないうちに、ドイツのAmazon EC2にいる誰かが、避けるべきレコードを探すかのように自分のドメインの一部にゾーン転送を試み、その後は自分のドメインを完全に除外し、スキャンもすぐ止まった
      発信元IPは世界中に散らばっていたが、実際のスキャンノイズは たった1人 から出ていたことになる
    • AWSがインスタンスメタデータサービス(IMDS)にfail2banを走らせる理由がわからない。実装を信用していないのか、大口顧客から訴えられたいのかも疑問
  • IPベースの遮断は注意が必要。IP帯域は時々再割り当てされるため、無関係な人を遮断してしまうことがあり、地域やデータセンターという理由で遮断していた帯域が住宅向けISPに移った例も何度も見た
    HTTP/1.1の遮断も、古いブラウザを使う実際のユーザーをブロックしてしまう危険が大きい。さらに、クロスオリジンのリクエストで完全なURLを送らないブラウザもあるため、Google検索から流入した場合でもリファラがGoogleのルートページしか指さないことがあり、これをボットの偽装と決めつけて遮断すると、Google検索からの流入まで消える可能性がある

    • 自分でも知らないうちにネットワークが住宅向けVPNに再販されている人も気の毒なくらい多い
      一方で、HTTP/1.1の遮断は合理的だと思う。ほぼすべてのブラウザがそれを超えるプロトコルをサポートしてから10年以上経っており、そのほど古いブラウザなら、すでに主流のWebの大半が壊れているので、個人サイト1つがさらに使えなくても例外ではなく日常だろう
    • 趣味や実験用のサイトでは、GoogleのすべてのASNを完全に遮断している。最近は有益なトラフィックを受けたことがなく、検索品質も壊れたと見ている
      古いブラウザやAPIツールを取りこぼしても、HTTP/1.1の遮断は維持する予定。古い金融システムの独占コードなら理解できるが、公開インターネットは自分のために更新されるべきだ
      Googleはずっと前から遮断しているので、Googleから来たと主張するリクエストはすべて偽りだ。ブログを複数のランダムなドメインでローテーションして関連性とスナップショットを断ち、人々が自分の文章を見つける経路を制御しようとしている
    • 数年前、AWSから入ってくるトラフィックを遮断して記事を書いた。普段の実訪問者は週に数十人しかいないのに、その記事は約3か月の間に実際の人間15,000人に読まれ、その後すぐ忘れ去られた
      そのおかげで無料の侵入テストも受けられ、防御策と処理パイプラインが堅牢だという結論を得た。生存圧力のため、ボットトラフィックの90%がVPNへ移動し、VPN終端点がクリスマスツリーのように光っている
      使い捨てのアクセスIPフィードを提供することもできるが、利用者はきちんと検証を受け、用途も承認されなければならない。この過程自体が面白い
    • 筆者は複数種類の実ユーザーを遮断しても構わないと明言しているので、その助言には従わない
  • 読めないなら、アーカイブ https://archive.ph/d3236 で見られる

    • 一般的なiOS Safariユーザーには残念な実装だ: https://i.ibb.co/vCDH79d0/IMG-0303.png
      ボットでもないのに、何かを読むためにiCloud Private Relayを切りたくはない。筆者の別の回答によれば、試験用サイトとしては良い実装だが、ほかのWeb管理者は可能ならこのやり方をそのまま全部導入しないでほしい
    • 自分もアクセスできなかったのに、archive.phクローラは無事に通過したという点が面白い
  • 応答本文に410とSec-Fetch-Mode:文字列しか出ないのを見ると、自分をボットだと判定したようだ。読むものも見るものもなく、そのまま去るしかなく、現代のWebはひどい

    • 実際のブラウザはそのヘッダを送るが、一部のリーダーアプリやChrome Headlessを使わない大半のボットは送らない
      対応状況は https://caniuse.com/?search=sec-fetch-mode で、一部ヘッダは https://nochan.net/.env で確認できる
    • そこまで行くこともできず、TLSハンドシェイクすら通らなかったという PR_END_OF_FILE_ERROR が発生した
  • Webトラフィックの99%以上がボットやエージェントだと予想されるので、訪問者数表示を消そうか悩んでいる。数値に意味がなく、実際よりサイトがずっと賑わって見えるが、本物の人間が文章を読んだり本をダウンロードしたりできなくなるのではと心配で対応をためらっている

    • テクノスリラー・SF・ミステリー小説とは、あとで見てみたい
  • 遮断が必要なら、一般に許可リストは拒否リストより効果的で、許可リストを適用できないなら、この方式も良い解決策ではないかもしれない
    CloudflareやAnubisのようなツールは深刻なアクセシビリティ問題を起こしうるので、リクエスト頻度制限のほうを好む。アクセシビリティを損なわず、しかもすっきりしており、一時的な問題には短時間のIP遮断もうまく機能する
    個人的にはfail2banでHTTPログを分析し、robots.txtで禁止したURLやwp-login.phpのようなパスを要求したり、リクエスト頻度制限をあまりに頻繁に超えたりするIPをN時間遮断している。現在はGit Web UIでAnubisを試験中だ

    • 企業間通信では、ネットワーク間VPNで許可リストを実装した。VPNの外からはサーバーにアクセスできず、従業員は会社のVPN経由で接続できる
  • fail2banだけでもかなり防げるが、最初の数か月は環境に合わせて細かく調整する必要がある
    まず failregex = ^ - \\S+ \\[\\] ".*?" 40[034] フィルタで検知し、その後より具体的な一覧に追加したが、今では約80個の正規表現であらゆるトラフィックを遮断しており、一般的な 40[034] フィルタまで到達するリクエストは長いこと出ていない
    ただし、ロードバランサやプロキシの背後では実IPを取得する方法が必要なので、fail2banも原文の方式もどちらも面倒になる

    • ほとんどのL7ロードバランサは、実IPを含むヘッダを追加する機能を提供している。Webサーバーがそのヘッダを記録するよう設定すればよく、CDNが実IPを渡す方式と非常によく似ている
  • これらのコメントと自分の接続の試みを見ると、ボットだけでなく正常なトラフィックもすべて遮断しているように見える

    • コメントだけ見ると誤解するかもしれない。これまでに約2,600人と一部のボットは記事を見ることができた
      日曜日は、人々がWebを探索するときに使う変わったブラウザやアプリを最も多く見かけ、平日は普通の主流ブラウザが多いので良いテストになる