Patchright ディープダイブ:検出を回避するPlaywrightフォークの技術解剖

Patchrightが修正するCDPランタイムリーク、navigator.webdriver、TLS/JA3フィンガープリントの技術詳細と、住宅プロキシとの組み合わせでCloudflareやDataDomeを通過する合法的自動化の実装例を解説。

Patchright Deep-Dive: Running Undetected Playwright Against Modern Anti-Bot Stacks

Patchright ディープダイブ:検出シグナルの技術解剖と合法的自動化の実装

Playwrightは強力なブラウザ自動化ツールですが、そのままではnavigator.webdriverフラグやCDPランタイムのリークにより、CloudflareやDataDomeなどのボット検出システムに即座に識別されます。Patchright ディープダイブでは、この検出回避フォークがどのシグナルを修正し、どのシグナルを残すのか、そして住宅プロキシとの組み合わせでどのように合法的な自動化が実現するのかを技術的に解剖します。

本記事は、セキュリティリサーチャーとシニアスクレイピングエンジニアを対象に、Patchrightの内部動作、TLS/JA3フィンガープリントの整合性、そしてProxyHat住宅プロキシを使った実装例を解説します。

なぜこの問題が存在するのか:ボット検出の技術的背景

現代のボット検出システムは、単一のシグナルではなく、複数のレイヤーで指紋を収集します。ブラウザのJavaScript環境、TLSハンドシェイク、HTTP/2フレーミング、IPレピュテーション、そして行動分析が組み合わさって、リクエストが人間由来かどうかを判定します。

PlaywrightはChrome DevTools Protocol(CDP)を通じてブラウザを制御しますが、このCDP接続自体が検出シグナルを残します。Runtime.enableコマンドの呼び出しは、window.cdc_プレフィックスの変数や、Runtime.evaluateの実行痕跡を通じて、自動化の存在を露呈します。標準のPlaywrightはこれらのシグナルを隠蔽しません。

MDNのドキュメントに記載されている通り、navigator.webdriverプロパティはW3C WebDriver仕様で定義されており、自動化制御下のブラウザではtrueを返します。これは最も基本的な検出シグナルですが、多くの検出システムにとって最初の足がかりに過ぎません。

検出レイヤーの全体像

  • JavaScript環境:navigator.webdriver、chrome.runtime、permissions APIの不一致
  • CDPランタイムリーク:Runtime.enable呼び出し、cdc_変数、evaluate実行痕跡
  • コマンドラインフラグ:--enable-automation、--remote-debugging-port、その他の自動化フラグ
  • TLS/JA3フィンガープリント:ClientHelloの暗号スイート順序、拡張機能の順序
  • HTTP/2フレーミング:SETTINGS、WINDOW_UPDATE、PRIORITYフレームの順序と値
  • IPレピュテーション:データセンターIP、ASNの分類、過去の悪用履歴
  • 行動シグナル:マウス移動パターン、タイピング間隔、スクロール速度

Patchrightが修正する検出シグナル

Patchrightは、Playwrightのソースコードにパッチを当てることで、以下の検出シグナルを根本的に修正します。

1. navigator.webdriverの除去

Patchrightは--disable-blink-features=AutomationControlledフラグをデフォルトで注入し、navigator.webdrivertrueを返さないようにします。標準のPlaywrightでもこのフラグは追加可能ですが、Patchrightはそれをビルドレベルで統合し、他のフラグとの整合性を保ちます。

2. CDPランタイムリークの修正

最も重要な修正は、Runtime.enableの呼び出しに伴うリークの防止です。標準のPlaywrightは、ページのJavaScriptを実行するためにRuntime.enableを呼び出しますが、これによりPerformance.measureのタイミングが変化し、window.chromeオブジェクトの一部プロパティが欠落するなど、複数の副次シグナルが発生します。

Patchrightは、CDPの呼び出しパターンを変更し、Runtime.enableを使用せずにJavaScriptを実行する代替パスを実装します。これにより、検出スクリプトがRuntime.enableの副作用を探しても、何も見つからなくなります。具体的には、Page.createIsolatedWorldRuntime.evaluateの呼び出しタイミングを調整し、Runtime.enableの全体的な有効化を回避します。

3. コマンドラインフラグの整理

標準のPlaywrightは多数のコマンドラインフラグを渡しますが、その中には自動化を示唆するものが含まれます:

  • --enable-automation:Chromeの自動化バッジを表示
  • --remote-debugging-port:デバッグポートの公開
  • --disable-extensions:拡張機能の無効化(通常のChromeでは有効)
  • --disable-component-extensions-with-background-pages:バックグラウンド拡張の無効化

Patchrightはこれらのフラグを削除または置換し、実際のChromeインストールと同じフラグセットを使用します。これにより、navigator.appVersionchrome.runtimeの検査でも自動化の痕跡が見つかりません。

4. channel='chrome'による本物のChrome TLSスタック

Patchrightはchannel='chrome'をデフォルトで使用し、Chromiumバイナリではなく実際のChromeインストールを使用します。これは重要です。なぜなら、ChromeとChromiumのTLS ClientHelloは微妙に異なるからです。ChromeはGoogleの独自拡張を含むTLSスタックを使用し、Chromiumはオープンソース版のTLSスタックを使用します。検出システムはこの違いを識別できます。

実際のChromeバイナリを使用することで、JA3/JA4フィンガープリントが本物のChromeユーザーと一致します。JA4フォーマットでは、TLS拡張機能の順序と暗号スイートの構成がブラウザの正確な識別に使用されます。ChromiumのデフォルトClientHelloはChromeと異なる拡張順序を持つため、channel='chrome'の使用が必須となります。

具体的には、Chrome 120のClientHelloは22個の暗号スイートを特定の順序で提示し、拡張機能もapplication_settings、signed_certificate_timestamp、encrypted_client_helloなどの順序で並びます。Chromiumビルドではこの順序が異なる場合があり、JA3ハッシュ値の不一致を引き起こします。

Patchrightが修正しないもの:限界と代替アプローチ

PatchrightはJavaScript環境とCDPリークを修正しますが、以下の領域はパッチの対象外です:

検出シグナル Patchright Camoufox playwright-stealth
navigator.webdriver 修正済み 修正済み 部分修正
CDP Runtime.enable リーク 修正済み N/A(Firefoxベース) 未修正
コマンドラインフラグ 修正済み 修正済み 部分修正
Canvas フィンガープリント 未修正 修正済み(ノイズ注入) 未修正
WebGL レンダラー情報 未修正 修正済み 未修正
フォント列挙 未修正 修正済み 未修正
行動シグナル(マウス等) 未修正 部分修正 未修正
TLS/JA3 フィンガープリント Chrome一致(channel使用時) Firefox一致 未修正

playwright-stealthの限界

playwright-stealthは、Playwrightの起動後にJavaScriptを注入してnavigator.webdriverを上書きするアプローチを取ります。しかし、この注入自体がCDPのRuntime.evaluateを通じて行われるため、注入の痕跡が残ります。また、Runtime.enableはすでに呼び出されているため、リークは防止できません。stealthプラグインは「事後補修」であり、Patchrightの「事前防止」アプローチとは根本的に異なります。

Camoufoxの強みと弱み

CamoufoxはFirefoxベースのフォークで、Canvas/WebGLフィンガープリントにノイズを注入する機能を持ちます。FirefoxのCDPを使用しないため、Runtime.enableリークの問題は存在しません。ただし、Firefoxベースであるため、Chromeユーザーとして偽装することはできず、ターゲットサイトのトラフィック分布がChrome中心の場合、Firefoxのトラフィック自体が少数派となり疑念の対象になる可能性があります。全球のブラウザシェアがChrome約65%、Firefox約3%であることを考慮すると、ChromeベースのPatchrightの方がトラフィックに溶け込みやすいと言えます。

CDPリークを閉じてもIPレピュテーションで弾かれる理由

PatchrightでJavaScript環境とCDPリークを完全に修正しても、Cloudflare TurnstileやDataDomeは依然としてブロックする可能性があります。その理由は、IPレピュテーションスコアが独立した検出レイヤーとして機能するからです。

Cloudflareのボット管理ドキュメントで説明されている通り、IPレピュテーションは、ASNの分類、過去の悪用履歴、IPの年齢、データセンターIPか住宅IPかの判定を含む多層的な評価です。データセンターIP(AWS、GCP、Azureなど)からのリクエストは、JavaScript環境が完璧であっても、初期スコアが低く設定されます。

具体的には、以下のIP属性がスコアに影響します:

  • ASNタイプ:ISP(住宅)vs ホスティングプロバイダー(データセンター)
  • IP年齢:新規割り当てIPは信頼スコアが低い
  • 過去の悪用履歴:同じIP/サブネットからの過去の攻撃履歴
  • 地理的整合性:IPのGeoIP位置とブラウザの言語/タイムゾーンの一致

これが、住宅プロキシが必須となる理由です。住宅プロキシはISPから割り当てられたIPを使用するため、検出システムにとって通常の家庭からのアクセスと区別がつきません。データセンタープロキシでは、たとえブラウザフィンガープリントが完璧であっても、IPレピュテーションだけでブロックされる確率が50%以上に達することがあります。

TLS/HTTP2フィンガープリントの整合性

ブラウザフィンガープリントとIPレピュテーションが一致しても、TLSフィンガープリントが整合しなければ、検出システムは不整合を検出します。例えば、Chrome 120のClientHelloを送信しているのに、IPがロシアの住宅IPで、Accept-Languageヘッダーがja-JPの場合、複数のシグナルが矛盾し、リスクスコアが上昇します。

整合性を保つための原則:

  1. TLS ClientHello:channel='chrome'を使用し、実際のChromeのClientHelloを送信する
  2. HTTP/2フレーミング:ChromeのSETTINGS、WINDOW_UPDATE、PRIORITYフレームの順序と値を維持する
  3. Accept-Language:プロキシのGeoIP位置と一致する言語を設定する
  4. タイムゾーン:プロキシのGeoIP位置と一致するタイムゾーンを設定する
  5. IP ASN:住宅プロキシでISP割り当てIPを使用する

Patchrightはchannel='chrome'によりTLSレベルの整合性を提供しますが、HTTPヘッダーの整合性は開発者の責任です。Accept-Language、Sec-Ch-Ua、Sec-Ch-Ua-Platformなどのヘッダーを、プロキシの地理的位置と一致させる必要があります。米国の住宅IPを使用する場合、Accept-Language: en-US,en;q=0.9timezone: America/New_Yorkの組み合わせが自然です。

実装例:Patchright + ProxyHat住宅プロキシ

以下は、Patchrightの永続コンテキストをProxyHat住宅プロキシ経由で使用する、合法的な公開データ収集の実装例です。米国の固定セッションIPを使用し、保護されたページにアクセスします。

Python実装

from patchright.sync_api import sync_playwright

# ProxyHat住宅プロキシの設定
# gate.proxyhat.com:8080 は HTTP プロキシポート
# ユーザー名に国とセッションIDを指定して固定IPを取得
proxy_config = {
    "server": "http://gate.proxyhat.com:8080",
    "username": "user-country-US-session-abc123",
    "password": "YOUR_PASSWORD"
}

with sync_playwright() as p:
    # 永続コンテキストで実際のChromeを使用
    context = p.chromium.launch_persistent_context(
        user_data_dir="/tmp/patchright-profile",
        channel="chrome",
        proxy=proxy_config,
        headless=False,
        locale="en-US",
        timezone_id="America/New_York",
        viewport={"width": 1920, "height": 1080},
        args=[
            "--disable-blink-features=AutomationControlled",
        ]
    )

    page = context.new_page()

    # Accept-Languageを米国に合わせる
    page.set_extra_http_headers({
        "Accept-Language": "en-US,en;q=0.9",
    })

    # 保護されたページへアクセス
    page.goto("https://example.com/protected", wait_until="networkidle")

    # ページ内容の取得
    title = page.title()
    print(f"ページタイトル: {title}")

    context.close()

Node.js実装

const { chromium } = require('patchright');

const proxyConfig = {
    server: 'http://gate.proxyhat.com:8080',
    username: 'user-country-US-session-abc123',
    password: 'YOUR_PASSWORD'
};

(async () => {
    const browser = await chromium.launchPersistentContext(
        '/tmp/patchright-profile',
        {
            channel: 'chrome',
            proxy: proxyConfig,
            headless: false,
            locale: 'en-US',
            timezoneId: 'America/New_York',
            viewport: { width: 1920, height: 1080 },
            args: ['--disable-blink-features=AutomationControlled']
        }
    );

    const page = await browser.newPage();
    await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
    await page.goto('https://example.com/protected', { waitUntil: 'networkidle' });

    console.log('ページタイトル:', await page.title());
    await browser.close();
})();

curlでの接続テスト

# ProxyHat HTTP プロキシ経由でアクセス
curl -x "http://user-country-US-session-abc123:YOUR_PASSWORD@gate.proxyhat.com:8080" \
  -H "Accept-Language: en-US,en;q=0.9" \
  "https://example.com/protected"

SOCKS5を使用する場合

# SOCKS5 プロキシ(ポート1080)
curl -x "socks5://user-country-US-session-abc123:YOUR_PASSWORD@gate.proxyhat.com:1080" \
  -H "Accept-Language: en-US,en;q=0.9" \
  "https://example.com/protected"

セッションID abc123を指定することで、同じIPからのリクエストとして認識され、IPローテーションによるセッション切断を防ぎます。セッションIDを変更すれば、新しいIPに切り替わります。詳細な設定についてはProxyHat ドキュメントを参照してください。

よくある間違いとエッジケース

1. headless=Trueの使用

Headless ChromeはHeadful Chromeとは異なる動作を示します。navigator.webdriver以外にも、window.outerWidth === 0navigator.plugins.length === 0など、複数のシグナルでHeadlessが識別されます。可能な限りheadless=Falseを使用し、サーバー環境ではXvfbなどの仮想ディスプレイを使用します。Headlessモードでは300ms以上のレイテンシ差が検出されることもあります。

2. タイムゾーンとGeoIPの不一致

プロキシが米国のIPを提供しているのに、ブラウザのタイムゾーンがAsia/Tokyoの場合、検出システムは不整合を検出します。上記の実装例のように、timezone_idをプロキシの地理的位置に合わせることが重要です。Intl.DateTimeFormat().resolvedOptions().timeZoneがIPのGeoIPと一致しない場合、DataDomeは即座にチャレンジを表示します。

3. Accept-LanguageとIP地理の不一致

同様に、米国IPでAccept-Language: ja-JP,ja;q=0.9を送信すると、不整合シグナルとなります。プロキシの国に合わせて適切な言語ヘッダーを設定してください。

4. セッションの過剰使用

1つのセッションIPで短期間に大量のリクエストを送信すると、IPレピュテーションが低下します。適切なレート制限(1秒あたり2〜5リクエスト程度)と、リクエスト間のランダムな待機時間を設定してください。100回以上の連続リクエストで同じセッションを使用すると、行動パターン分析の対象になる可能性があります。

5. Canvas/WebGLフィンガープリントの未対応

PatchrightはCanvas/WebGLフィンガープリントを修正しないため、同一プロファイルで多数のサイトにアクセスすると、一貫したCanvasハッシュが追跡可能になります。プロファイルを定期的に再作成するか、Camoufoxとの併用を検討してください。Canvasハッシュは1回のtoDataURL()呼び出しで取得でき、デバイスを一意に識別する強力なシグナルとなります。

6. プロキシのプロトコル不整合

HTTPプロキシ(ポート8080)とSOCKS5プロキシ(ポート1080)を混同しないでください。Patchrightのproxy.server設定と、実際のプロキシポートが一致していることを確認してください。HTTPプロキシはCONNECTメソッドでトンネリングしますが、SOCKS5は異なるハンドシェイクを使用します。

適切な利用範囲:合法的自動化とセキュリティリサーチ

Patchrightと住宅プロキシの組み合わせは、以下の用途で適切に使用すべきです:

  • 公開データの収集:robots.txtを尊重し、利用規約に違反しない範囲での公開コンテンツ収集
  • セキュリティリサーチ:認可されたペネトレーションテスト、脆弱性調査
  • QA自動化:自社アプリケーションのE2Eテスト
  • SERP追跡:検索エンジン結果ページの監視(SERP追跡の詳細
  • 価格監視:競合価格の公開情報収集(Webスクレイピングの詳細

以下の用途には使用しないでください:

  • ログイン壁の背後にある非公開データへの不正アクセス
  • クレジットカード詐欺、アカウント乗っ取り
  • チケットやスニーカーの不正転売目的での大量購入
  • 利用規約に明示的に禁止されている自動化

GDPRやCCPAなどのデータ保護規制も考慮が必要です。個人データの収集は、法的根拠と適切な通知を伴う必要があります。GDPRの第6条に基づく適法な利益の根拠がある場合でも、データ主体の権利を尊重する必要があります。

ProxyHatの設定とロケーション

ProxyHatは住宅プロキシ、モバイルプロキシ、データセンタープロキシを提供しています。Patchrightとの組み合わせでは、住宅プロキシが推奨されます。料金プランは用途に応じて選択でき、利用可能なロケーションは世界中に広がっています。

国指定はユーザー名で行います:user-country-USuser-country-DE-city-berlinなど。都市レベルのターゲティングも可能で、より精密な地理的整合性を実現できます。セッションIDを付与することで、最大30分間同じIPを維持できます。

Key Takeaways

  • Patchrightはnavigator.webdriver、CDP Runtime.enableリーク、コマンドラインフラグを修正するが、Canvas/WebGL/フォント/行動シグナルは未対応
  • channel='chrome'の使用が必須:ChromiumとChromeのTLS ClientHelloは異なり、JA3ハッシュ値が一致しない
  • CDPリークを修正してもIPレピュテーションでブロックされる可能性が50%以上:住宅プロキシが必須
  • TLSフィンガープリント、Accept-Language、タイムゾーン、IP地理の整合性をすべて揃える必要がある
  • セッションIDで固定IPを維持し、適切なレート制限(1秒あたり2〜5リクエスト)とランダム待機を組み合わせる
  • 合法的な公開データ収集と認可されたセキュリティリサーチでのみ使用する

FAQ

Patchright ディープダイブとは何ですか?

Patchright ディープダイブとは、PlaywrightのフォークであるPatchrightがどのようにボット検出シグナルを修正するかを技術的に深く分析することです。具体的には、navigator.webdriverの除去、CDP Runtime.enableリークの修正、コマンドラインフラグの整理、channel='chrome'による本物のChrome TLS/JA3スタックの使用などを含みます。これにより、標準のPlaywrightでは即座に検出されるシグナルを根本的に解消できます。

なぜPatchrightはプロキシユーザーにとって重要なのですか?

Patchrightはブラウザレベルの検出シグナルを修正しますが、IPレピュテーションは独立した別のレイヤーです。データセンターIPはCloudflareやDataDomeで低い信頼スコアを与えられ、ブラウザフィンガープリントが完璧でもブロックされる可能性が50%以上あります。住宅プロキシとPatchrightの組み合わせで初めて、ブラウザとIPの両方の整合性が確保され、検出システムを通過できる確率が大幅に向上します。

Patchrightに最適なプロキシタイプは何ですか?

住宅プロキシが最適です。ISPから割り当てられたIPアドレスを使用するため、検出システムにとって通常の家庭からのアクセスと区別がつきません。データセンタープロキシは低いIPレピュテーションスコアを受け、モバイルプロキシはIPが頻繁に変わるためセッション安定性が必要な用途には不向きな場合があります。Patchrightの固定セッション機能と組み合わせることで、地理的整合性とセッション安定性を同時に確保できます。

Patchrightの実装でブロックを回避するにはどうすればよいですか?

channel='chrome'で実際のChromeを使用し、プロキシの地理的位置にAccept-Languageとタイムゾーンを合わせ、headless=False(またはXvfb)を使用し、セッションIDで固定IPを維持し、適切なレート制限(1秒あたり2〜5リクエスト)を設定し、ランダムな待機時間を挟んでください。Canvas/WebGLフィンガープリントは修正されないため、プロファイルを定期的に再作成することも重要です。すべてのレイヤーで整合性を保つことが成功の鍵です。

始める準備はできましたか?

AIフィルタリングで148か国以上、5,000万以上のレジデンシャルIPにアクセス。

料金を見るレジデンシャルプロキシ
← ブログに戻る