Playwrightで自動化スクリプトを書いた経験があるエンジニアなら、navigator.webdriverがtrueを返す瞬間にアクセスをブロックされたことがあるだろう。Cloudflare Turnstile、DataDome、Imperva——これらの最新ボット検知システムは、CDPランタイムのリークからTLSフィンガープリントの不一致まで、多層的なシグナルで自動化を検出する。本記事ではPatchright徹底解説として、検出回避Playwrightフォークの技術的内部構造から、住宅プロキシとの統合実装までを深く掘り下げる。
Patchrightは、Playwrightのソースコードを直接修正し、自動化を示すシグナルをバイナリレベルで除去するフォークだ。JSインジェクションによる事後補正ではなく、Playwrightのビルド時点でパッチを適用するため、検知側が観測するタイミングよりも前にシグナルが消える。この違いが、CloudflareやDataDomeのような高度な検知システムに対する成功率を大きく変える。
Patchright徹底解説:検出シグナルの技術的分解
Patchrightが対象とする検出シグナルは、大きく4つの層に分類できる。それぞれのシグナルがどのように検出され、Patchrightがどのように修正するかを順に見ていく。
navigator.webdriverの除去
W3C WebDriver仕様では、自動制御下のブラウザはnavigator.webdriverプロパティをtrueに設定することが求められる。Playwrightはこの仕様に従い、デフォルトでtrueを返す。これは最も古典的な検出シグナルであり、多くのボット検知システムが最初にチェックする項目だ。
MDNのドキュメントにある通り、このプロパティは読み取り専用で、単純なJS上書き(Object.defineProperty)では検知側に補足される。Patchrightは、Playwrightのソースコードレベルでこのプロパティの設定ロジックを削除する。これにより、プロパティが存在しないかfalseを返す状態になり、JSインジェクションの痕跡も残らない。
CDP Runtime.enableリークの修正
PlaywrightがChrome DevTools Protocol(CDP)経由でブラウザを制御する際、Runtime.enableコマンドを送信する。このコマンドは、JavaScript実行コンテキストを作成・管理するために必要だが、副作用として検出可能なアーティファクトを残す。具体的には以下のシグナルが発生する。
- Error().stackのトレースにCDP由来のフレームが含まれる
console.debugのバインディングがネイティブコードでなくなるRuntime.evaluate経由で注入されたスクリプトのスタックトレースに異常が現れる
これらのシグナルは、ページのJavaScriptから観測可能であり、高度な検知システムはError().stackの内容を解析してCDPの使用を判定する。Patchrightは、Runtime.enableの呼び出し方法を変更し、これらのリークを防ぐ。具体的には、Runtime.enableを使用せずにスクリプト注入を実現する代替パスを実装することで、スタックトレースにCDPの痕跡が残らないようにする。
コマンドラインフラグの除去と--disable-blink-features=AutomationControlled
PlaywrightはChromeを起動する際、--enable-automationフラグを渡す。このフラグはChromeのオートメーションインジケーター(アドレスバーの「Chromeは自動テストソフトウェアによって制御されています」表示)を有効にするが、同時にnavigator.webdriver = trueの設定や、Blinkレンダリングエンジン内の自動化フラグも有効にする。
Patchrightは--enable-automationを削除し、代わりに--disable-blink-features=AutomationControlledを注入する。このフラグは、BlinkエンジンのAutomationControlled機能フラグを無効化し、navigator.webdriverがfalseを返すようにする。重要なのは、このフラグが実際のChromeユーザーの起動引数にも使われる正当なフラグであることだ。つまり、このフラグの存在自体は検出シグナルにならない。
channel='chrome'によるリアルChrome TLSスタックの使用
PlaywrightのデフォルトではChromiumバイナリを使用するが、ChromiumのTLSスタックは本物のChromeと微妙に異なる場合がある。Patchrightはchannel='chrome'をデフォルトとして推奨し、システムにインストールされた本物のGoogle Chromeバイナリを使用する。これにより、TLS ClientHelloの暗号スイート順序、拡張機能の順序、ALPNプロトコルリストが、実際のChromeユーザーと完全に一致する。
例えば、Chrome 120のTLS ClientHelloは、TLS 1.3の暗号スイートとしてTLS_AES_128_GCM_SHA256 (0x1301)、TLS_AES_256_GCM_SHA384 (0x1302)、TLS_CHACHA20_POLY1305_SHA256 (0x1303)をこの順序で提示する。JA3ハッシュはこれらの順序に依存するため、ChromiumとChromeのわずかな差異でも異なるJA3ハッシュが生成される。Playwrightのブラウザ設定ドキュメントでchannelオプションの詳細を確認できる。
Patchrightが修正するもの・しないもの
Patchrightは、Playwrightレイヤーの検出シグナルを幅広く修正するが、ブラウザレベルのフィンガープリントには手を触れない。この境界を理解することが、実装の成否を分ける。
修正されるシグナル
navigator.webdriver— バイナリレベルで除去- CDP
Runtime.enableリーク — 代替実装で回避 --enable-automationフラグ — 削除- Chrome自動化インジケーター —
--disable-blink-features=AutomationControlledで無効化 - TLS/JA3フィンガープリント —
channel='chrome'で本物のChromeスタックを使用 - HTTP/2 SETTINGSフレームの順序 — リアルChromeバイナリのものと一致
修正されないシグナル
- Canvas フィンガープリント — Canvas APIの描画結果はGPU、ドライバ、フォントレンダリングに依存し、Patchrightはこれを変更しない
- WebGL レンダラー文字列 —
WEBGL_debug_renderer_info拡張が返すベンダー/レンダラー文字列はそのまま - フォント列挙 — システムにインストールされたフォントのリストは変更されない。ヘッドレス環境ではフォントが少なく、検出シグナルになる
- AudioContext フィンガープリント — オーディオ処理のハードウェア依存の差異はそのまま
- 行動シグナル — マウス移動の軌跡、クリック間隔、スクロール速度、タイピングリズムなどの行動分析はPatchrightの範囲外
playwright-stealthとCamoufoxとの比較
検出回避のエコシステムには、Patchright以外にも選択肢がある。それぞれのアプローチの違いを理解することが重要だ。
| 機能 | Patchright | playwright-stealth | Camoufox |
|---|---|---|---|
| navigator.webdriver | バイナリレベルで修正 | JS注入で上書き | C++レベルで修正 |
| CDP Runtime.enableリーク | 回避 | 未対応 | 該当なし(Firefox) |
| --enable-automationフラグ | 削除 | 未対応 | 該当なし |
| リアルChrome TLS/JA3 | channel='chrome'で対応 | 未対応(Chromium使用) | Firefoxベース(異なるTLS) |
| Canvas/WebGLフィンガープリント | 未対応 | 未対応 | C++レベルで修正 |
| フォント列挙の偽装 | 未対応 | 未対応 | 対応 |
| 行動シグナル | 未対応 | 未対応 | 未対応 |
playwright-stealthは、ページ読み込み後にJSを注入してnavigator.webdriverなどを上書きするアプローチだ。実装は簡単だが、JS注入のタイミングが検出より遅い場合があり、CDPレベルのリークには対応しない。
Camoufoxは、FirefoxのC++ソースコードレベルでCanvas、WebGL、フォントのフィンガープリントを修正する。ブラウザレベルの偽装では最も強力だが、Firefoxベースであるため、Chromeが市場シェアの大部分を占める環境では、ブラウザシグネチャ自体が目立つ可能性がある。
Patchrightの位置づけは、「Chromeの正体を保ちつつ、自動化の痕跡のみを消す」ことだ。CanvasやWebGLの偽装が必要な場合は、Patchrightの上に追加のJSレイヤーを積むか、Camoufoxを検討する必要がある。
IPレピュテーション:CDPリーク修正後も残る検出層
ブラウザのフィンガープリントを完璧に整えても、接続元のIPアドレスがデータセンターレンジ(AWS、GCP、DigitalOceanなど)であれば、Cloudflare TurnstileやDataDomeはIPレピュテーションスコアで高リスクと判定する。
CloudflareのTurnstile発表記事で説明されている通り、TurnstileはIPレピュテーション、ブラウザの整合性、行動シグナルを組み合わせてリスクスコアを算出する。ブラウザシグナルがクリーンでも、IPが過去にボットトラフィックに関連付けられていれば、チャレンジやブロックが発生する。
DataDomeも同様に、IPレピュテーションデータベースを用いて、データセンターIP、VPN出口、既知のプロキシサーバーからのリクエストに高リスクスコアを付与する。これは、CDPリークを修正したPatchrightを使用しても、データセンタープロキシ経由であれば検出されることを意味する。データセンタープロキシの平均レイテンシは50-100msと低いが、IPレピュテーションの観点では不利になる。
ここで住宅プロキシが不可欠になる。住宅プロキシは、ISPから割り当てられた実際の住宅IPアドレスを使用するため、IPレピュテーションデータベースで「住宅ユーザー」として分類される。CloudflareやDataDomeは、住宅IPからのリクエストに対して、データセンターIPよりもはるかに低いリスクスコアを付与する。住宅プロキシの平均レイテンシは200-500msとデータセンターより高いが、検知回避の観点ではこのトレードオフは許容できる。
ただし、住宅プロキシの品質にはばらつきがある。IPが短期間で頻繁に交代するローテーションプロキシの場合、同じIPが多数のリクエストを短時間に送信しているように見える場合があり、これ自体が異常シグナルになる。スティッキーセッション(同一IPを一定期間維持)をサポートするプロキシを選ぶことが重要だ。ProxyHatでは、ユーザー名にsession-abc123のようなセッションIDを指定することで、最大72時間同じ住宅IPを維持できる。
TLS/HTTP2フィンガープリントの整合性
ボット検知システムは、TLS ClientHelloから生成されるJA3/JA4ハッシュと、HTTP/2 SETTINGSフレームから生成されるAkamaiフィンガープリントを用いて、クライアントの正体を判定する。これらのフィンガープリントは、ブラウザのバージョンとOSに固有で、検知側は既知のブラウザのフィンガープリントデータベースと照合する。
整合性の問題は、プロキシがTLSを終端する場合に発生する。一部のプロキシ(特にMITM型のプロキシ)は、クライアントとプロキシ間、プロキシとターゲット間で別々のTLSセッションを確立する。この場合、ターゲットサーバーが観測するTLS ClientHelloはプロキシのものになり、ブラウザのフィンガープリントと一致しない。
Patchright + channel='chrome' + 住宅プロキシの組み合わせが強力な理由は以下の通りだ。
- Patchrightが
channel='chrome'で本物のChromeバイナリを使用 → TLS ClientHelloが実際のChromeと一致 - ProxyHatの住宅プロキシはTLSを終端しない(エンドツーエンドのTLSトンネル)→ ブラウザのClientHelloがそのままターゲットに到達
- 住宅IPの出口から接続 → IPの地理的位置とTLSフィンガープリントの組み合わせが「リアルなChromeユーザー」として整合
逆に、データセンタープロキシ + Chromiumの組み合わせでは、JA3ハッシュがChromeの既知のパターンと微妙に異なり、かつIPがデータセンターレンジであるため、2つのシグナルが同時に「ボット」を示すことになる。99.9%の稼働率で安定稼働するプロキシインフラであっても、フィンガープリントの不整合があれば検知される。
実装例:Patchright + ProxyHat住宅プロキシ
以下に、Patchrightの永続コンテキストをProxyHatの住宅プロキシ経由でルーティングする実装例を示す。スティッキーセッションで米国の住宅IPを固定し、保護されたページにアクセスする。
Python実装
from patchright.sync_api import sync_patchright
# ProxyHat住宅プロキシ — 米国スティッキーセッション
proxy_url = "http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080"
with sync_patchright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir="/tmp/patchright-profile",
channel="chrome",
proxy={"server": proxy_url},
headless=False,
no_viewport=True,
args=["--disable-blink-features=AutomationControlled"]
)
page = context.new_page()
page.goto("https://example.com", wait_until="networkidle")
title = page.title()
print(f"Page title: {title}")
context.close()この例の重要なポイント:
channel="chrome"— 本物のChromeバイナリを使用し、TLS/JA3フィンガープリントを実際のChromeと一致させるuser-country-US-session-abc123— 米国の住宅IPをスティッキーセッションで固定。同じIPで複数ページを遷移できるlaunch_persistent_context— CookieやlocalStorageをプロファイルに保存し、再訪時にセッションを維持no_viewport=True— ウィンドウサイズを明示的に設定せず、OSのデフォルトを使用。固定ビューポートは自動化のシグナルになる
curlでの接続確認
Patchrightを実行する前に、プロキシ接続とIPの地理的位置を確認する。
curl -x http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080 \
https://ipinfo.io/jsonこれにより、出口IPが米国のISP IPであることを確認できる。レスポンスのcountryフィールドがUS、orgフィールドにISP名が含まれていれば、住宅IPとして機能している。
Node.js実装
const { chromium } = require('patchright');
const proxyUrl = 'http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080';
(async () => {
const browser = await chromium.launchPersistentContext('/tmp/patchright-profile', {
channel: 'chrome',
proxy: { server: proxyUrl },
headless: false,
noViewport: true,
args: ['--disable-blink-features=AutomationControlled']
});
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
console.log('Title:', await page.title());
await browser.close();
})();SOCKS5での接続
一部の環境ではSOCKS5プロトコルが必要な場合がある。ProxyHatはポート1080でSOCKS5をサポートしている。
proxy_url = "socks5://user-country-US-session-abc123:pass@gate.proxyhat.com:1080"SOCKS5はHTTPプロキシよりもオーバーヘッドが少なく、UDPリレーもサポートするが、大部分のユースケースではHTTPプロキシ(ポート8080)で十分である。100 concurrent sessions以上の並行接続が必要な場合は、セッションIDを分散させて複数の住宅IPを同時に使用できる。
適切な利用範囲と倫理的境界
Patchrightと住宅プロキシの組み合わせは強力なツールだが、その利用は適切な範囲に留める必要がある。以下は明確に許容される利用範囲と、そうでないものの境界だ。
適切な利用
- 認可されたセキュリティ研究 — 自社所有のアプリケーションのペネトレーションテスト、バグバウンティプログラムの範囲内でのテスト
- 公開データの収集 — robots.txtを尊重し、利用規約で禁止されていない公開ページのスクレイピング
- QA自動化 — 自社のステージング/本番環境に対するE2Eテスト
- SERP追跡 — 検索エンジンのランキングデータ収集(APIが利用できない場合)
公開データの収集については、Webスクレイピングのユースケースを参照し、SERP追跡の実装についてはSERP追跡のユースケースを参照してほしい。
不適切な利用
- ログイン壁の背後にあるデータへの不正アクセス
- チケットやスニーカーの不正購入(ボット購入)
- クレジットカード不正使用のテスト
- 利用規約で明示的に禁止されているスクレイピング
- GDPR/CCPAに違反する個人データの収集
詳細なプロキシロケーションはProxyHatのロケーション一覧で確認でき、料金体系はProxyHatの料金ページで確認できる。実装の詳細についてはProxyHatのドキュメントを参照してほしい。
Key Takeaways
Patchrightは、Playwrightのバイナリレベルで自動化シグナルを除去する。JSインジェクションではなくソースコード修正によるため、検出より前にシグナルが消える。
- navigator.webdriver、CDP
Runtime.enableリーク、--enable-automationフラグは、Patchrightが修正する主要な検出シグナルだ - Canvas、WebGL、フォント、行動シグナルはPatchrightの範囲外。必要な場合は追加のJSレイヤーまたはCamoufoxを検討する
- IPレピュテーションは、ブラウザフィンガープリントが完璧でも検出を引き起こす。Cloudflare TurnstileやDataDomeに対しては住宅プロキシが必須
- TLS/JA3の整合性は、
channel='chrome'+ TLSを終端しない住宅プロキシの組み合わせで実現する - 倫理的境界を守る。認可されたセキュリティ研究と公開データの収集に限定し、ログイン壁の背後のデータや利用規約違反のスクレイピングは行わない






