PerimeterX検出の技術的全体像
PerimeterX(現HUMAN Security)は、航空券サイトやハイエンドECサイトを中心に導入されているボット防御プラットフォームです。単一の検出シグナルではなく、TLSフィンガープリント、デバイスフィンガープリント、IPレピュテーション、行動シグナルを組み合わせた多層検出が特徴で、中でも行動分析の比重が他のWAF/Bot Mitigationと比べて際立って高い点が特筆されます。
スクレイピングエンジニアにとって、PerimeterXを「ただのCAPTCHA」と捉えるのは重大な誤解です。本稿では、PerimeterXの検出アーキテクチャをシグナルレベルで解剖し、正当な自動化・セキュリティ研究の文脈で検出をクリーンに通過するための具体的な手法を解説します。
PerimeterXのJSチャレンジフローと_px3/_pxhd Cookie
PerimeterXの防御は、ブラウザが最初のリクエストを送った瞬点から始まります。全体のフローは以下の通りです:
- 初期リクエスト:ブラウザがHTMLページをリクエスト。サーバーはページHTML内にインラインのPerimeterX JavaScriptスニペットを埋め込んで返します。
- JSチャレンジ実行:スニペットが追加の外部JS(
_px名前空間のオブジェクトを定義)を読み込み、デバイスフィンガープリント採取・行動データ収集を実行。 - ペイロード送信:収集されたシグナルが暗号化・難読化されてPerimeterXエンドポイントにPOSTされます。
- Cookie発行:シグナル評価の結果に基づき、
_px3(旧形式)または_pxhd(新形式)Cookieが発行されます。_pxhdはHTTP OnlyかつSecure属性付きで、セッション単位の認証トークンとして機能します。 - 後続リクエスト:Cookieが有効であれば、以降のAPIリクエストやページ遷移は通常通り処理されます。
重要なポイント: _pxhd Cookieが「信頼済み」とマークされていても、PerimeterXは全リクエストで行動シグナルを継続的に評価します。一度通過したからといって、その後のリクエストで検出されないわけではありません。
チャレンジ失敗時には、以下のいずれかの応答が返ります:
- インタースティシャルCAPTCHA:画像選択型のCAPTCHAページにリダイレクト
- ブロックページ:403レスポンスと共に「Access Denied」ページ
- サイレントブロック:HTTP 200だが内容が空、またはダミーデータを返すハニーポット応答
PerimeterXの検出シグナル詳細
PerimeterXは単一シグナルではなく、複数のシグナルを統合したスコアリングモデルで判定を行います。各シグナルの技術的な中身を見ていきましょう。
TLS/JA3/JA4フィンガープリント
TLSハンドシェイクの際、クライアントはClientHelloメッセージで対応する暗号スイートのリストを送信します。このリストの順序と内容が、クライアントを一意に識別する「TLSフィンガープリント」となります。
- JA3:TLSバージョン、暗号スイート、拡張、楕円曲線、楕円曲線ポイントフォーマットのMD5ハッシュ。例えばChrome 120のJA3は
771,4865-4866-4867...,で始まる特定の文字列になり、Pythonのrequestsライブラリは異なるJA3を生成します。 - JA4:JA3の後継で、TLS 1.3対応やより詳細なプロトコル特性をキャプチャ。PerimeterXはJA4ベースの検出も段階的に導入しています。
PythonのrequestsやhttpxのデフォルトTLSスタックは、ブラウザとは全く異なるJA3/JA4を生成するため、HTTPリクエストレベルで即座に検出されます。TLSフィンガープリントに関する詳細は別稿で解説しています。
デバイスフィンガープリント(Canvas、WebGL、画面メトリクス)
PerimeterXのJSチャレンジは、ブラウザ内で以下のフィンガープリントを採取します:
- Canvas API:特定のテキストと図形をCanvasに描画し、
toDataURL()のハッシュを取得。フォントレンダリング、サブピクセルスムージング、GPUドライバの差異が一意のシグナルになります。ヘッドレスChromeではCanvas描画に微妙な差異が生じ、検出の根拠となります。 - WebGL:
WEBGL_debug_renderer_info拡張からGPU情報(ベンダー・レンダラー文字列)を取得。加えて、WebGL描画テストのハッシュも採取。仮想GPUやソフトウェアレンダラーは即座にフラグが立ちます。 - 画面メトリクス:
screen.width、screen.height、devicePixelRatio、colorDepthの組み合わせ。ヘッドレスモードではscreenオブジェクトの値がwindowオブジェクトと一致しないなどの不整合が検出されます。 - フォント列挙:特定のフォントセットの描画幅を比較し、インストール済みフォント一覧を推定。
IPレピュテーションとジオロケーション
PerimeterXはIPアドレスを以下の軸で評価します:
- ASNタイプ:データセンターASN(AWS、GCP、DigitalOcean等)は高リスクスコア。住宅・モバイルASNは低リスク。
- 過去のボット活動:そのIP/サブネットからの過去の悪性トラフィック履歴。
- ジオ一貫性:IPのジオロケーションと、ブラウザの
navigator.language、Intl.DateTimeFormatのタイムゾーンが矛盾している場合、スコアが上昇。
行動シグナル(マウスムーブメント、タイミング、スクロール)
PerimeterXが他のBot Mitigationと最も異なるのは、行動シグナルの比重です。具体的には:
- マウス/タッチムーブメント:移動軌跡の速度・加速度・曲率を分析。直線的すぎる移動や、一定速度の移動はボット的と判定。人間のマウスは微小なジッターと加速度の変動を含みます。
- キーストロークタイミング:キー押下から離上までの時間(dwell time)とキー間隔(flight time)の分布。均一な間隔は機械的入力の兆候。
- スクロールパターン:ページ閲覧時のスクロール速度、停止位置、方向転換の有無。
- ページ滞在時間:リンククリックまでの時間が短すぎる(ミリ秒オーダー)場合、プログラマティッククリックと判定。
PerimeterXの行動分析の特徴: DataDomeがTLSフィンガープリントとHTTPヘッダーに重きを置くのに対し、PerimeterXは「ページ上で人間らしく振る舞っているか」を最も重視します。これは、TLSを完璧に偽装しても、行動シグナルが不自然であればブロックされることを意味します。
PerimeterX vs DataDome vs Akamai — 検出手法の比較
三大Bot Mitigationプロバイダーの検出アプローチを比較します:
| 検出シグナル | PerimeterX (HUMAN) | DataDome | Akamai Bot Manager |
|---|---|---|---|
| TLS/JA3フィンガープリント | 中程度の重み | 非常に高い重み | 高い重み(JA3+独自拡張) |
| デバイスフィンガープリント | 高い重み(Canvas/WebGL重視) | 高い重み | 高い重み(センサーAPI独自実装) |
| 行動シグナル(マウス・キーボード) | 最も高い重み | 低〜中程度 | 中程度(センサーデータ重視) |
| IPレピュテーション | 高い重み(ASNベース) | 高い重み(リアルタイムDB) | 高い重み(独自IPインテリジェンス) |
| HTTPヘッダー順序・内容 | 中程度 | 非常に高い重み | 高い重み |
| チャレンジ形式 | インラインJS + インタースティシャルCAPTCHA | インラインJS + CAPTCHA | センサーJS + 302リダイレクトループ |
| Cookie | _px3 / _pxhd | datadome | ak_bmsc / bm_sz |
この比較から分かるように、PerimeterXを通過するには行動シグナルの偽装が不可欠です。TLSやヘッダーを完璧に偽装しても、マウスムーブメントが機械的であればスコアが閾値を超え、ブロックに至ります。
PerimeterXを正当に通過するための具体的な対策
以下は、セキュリティ研究・正当な自動化の文脈でPerimeterXの検出をクリーンに通過するための5層アプローチです。
第1層:レジデンシャルプロキシによるIPレピュテーションの確保
データセンターIPはPerimeterXにとって最も検出が容易なシグナルです。ASNベースのフィルタリングが即座に機能するため、まずは住宅IPからのリクエストであることが前提となります。
ProxyHatのレジデンシャルプロキシを使用する例:
# curlでPerimeterX保護サイトにアクセス(米国住宅IP)
curl -x http://user-country-US:password@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" \
-H "Accept-Language: en-US,en;q=0.9" \
"https://www.example.com/"
ジオ一貫性を保つため、country-USでIPを指定した場合、ブラウザのnavigator.languageやタイムゾーンも米国に合わせる必要があります。この点については次の層で対処します。
第2層:Playwright Stealthによるブラウザフィンガープリントの偽装
ヘッドレスChromeのデフォルト設定には多数の検出可能なシグナルが含まれます。playwright-stealthプラグインは主要な検出ポイントをパッチしますが、PerimeterXに対しては追加の対策が必要です。
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
PROXY = {
"server": "http://gate.proxyhat.com:8080",
"username": "user-country-US-session-persist1",
"password": "PASSWORD"
}
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # PerimeterXはヘッドレス検出が強力
args=[
"--disable-blink-features=AutomationControlled",
"--disable-features=IsolateOrigins,site-per-process",
]
)
context = browser.new_context(
proxy=PROXY,
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York",
geolocation={"latitude": 40.7128, "longitude": -74.0060},
permissions=["geolocation"],
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
)
page = context.new_page()
stealth_sync(page)
# PerimeterXの_pxhd Cookie取得を待機
page.goto("https://www.example.com/", wait_until="networkidle")
# _pxhd Cookieが設定されるまで待機
page.wait_for_function(
"() => document.cookie.includes('_pxhd')",
timeout=30000
)
# Cookie取得後のAPIリクエスト等
print(page.content())
context.close()
browser.close()
重要なポイント:
headless=Falseで起動することで、Canvas/WebGLレンダリングが実際のGPUを使用し、ヘッドレス特有のフィンガープリント差異を回避timezone_idとlocaleをプロキシIPのジオロケーションと一致させるsession-persist1フラグでスティッキーセッションを使用し、_pxhdCookieの有効期間中は同じIPを維持
第3層:行動シグナルのリアリスティックなシミュレーション
PerimeterXの行動分析を通過するには、人間らしいマウスムーブメントとインタラクションが必要です。ベジエ曲線とランダムジッターを組み合わせたシミュレーションの例を示します:
import random
import math
from playwright.sync_api import Page
def human_like_mouse_move(page: Page, target_x: int, target_y: int):
"""ベジエ曲線とジッターで人間らしいマウス移動をシミュレート"""
current = page.mouse
viewport = page.viewport_size
start_x = random.randint(100, viewport["width"] - 100)
start_y = random.randint(100, viewport["height"] - 100)
# 3次ベジエ曲線の制御点を生成
cp1_x = start_x + (target_x - start_x) * random.uniform(0.2, 0.4) + random.gauss(0, 30)
cp1_y = start_y + (target_y - start_y) * random.uniform(0.1, 0.3) + random.gauss(0, 30)
cp2_x = start_x + (target_x - start_x) * random.uniform(0.6, 0.8) + random.gauss(0, 20)
cp2_y = start_y + (target_y - start_y) * random.uniform(0.7, 0.9) + random.gauss(0, 20)
steps = random.randint(30, 60)
for i in range(steps + 1):
t = i / steps
# 3次ベジエ曲線の補間
x = ((1-t)**3 * start_x +
3*(1-t)**2*t * cp1_x +
3*(1-t)*t**2 * cp2_x +
t**3 * target_x)
y = ((1-t)**3 * start_y +
3*(1-t)**2*t * cp1_y +
3*(1-t)*t**2 * cp2_y +
t**3 * target_y)
# 微小ジッターを追加(人間の手の震えをシミュレート)
if 0.1 < t < 0.9:
x += random.gauss(0, 1.5)
y += random.gauss(0, 1.5)
# 速度プロファイル:開始時は加速、終了時は減速
delay = max(2, int(8 * (1 - math.sin(t * math.pi / 2)) + random.uniform(0, 4)))
current.move(x, y)
page.wait_for_timeout(delay)
def human_scroll(page: Page, distance: int = 800):
"""人間らしいスクロールをシミュレート"""
remaining = distance
while remaining > 0:
scroll_amount = random.randint(30, 120)
page.mouse.wheel(0, scroll_amount)
remaining -= scroll_amount
page.wait_for_timeout(random.randint(50, 200))
def human_click(page: Page, selector: str):
"""人間らしいクリック:移動→短い停止→クリック"""
element = page.locator(selector)
box = element.bounding_box()
if not box:
return
target_x = box["x"] + box["width"] * random.uniform(0.2, 0.8)
target_y = box["y"] + box["height"] * random.uniform(0.2, 0.8)
human_like_mouse_move(page, int(target_x), int(target_y))
# クリック前の短い停止(人間の反応時間)
page.wait_for_timeout(random.randint(80, 250))
# ホバー→クリックの2段階
page.mouse.move(target_x, target_y)
page.wait_for_timeout(random.randint(30, 80))
page.mouse.click(target_x, target_y)
第4層:TLSフィンガープリントの偽装
PlaywrightのChromiumはブラウザと同等のTLSフィンガープリントを生成するため、追加の偽装は不要なことが多いですが、カスタムHTTPクライアントでAPIエンドポイントに直接アクセスする場合は注意が必要です。
curl-impersonateやtls-client(GoライブラリのPythonバインディング)を使うことで、ブラウザと同じJA3/JA4を再現できます。ただし、PerimeterXはTLSよりも行動シグナルを重視するため、Playwright経由でのアクセスが最も確実なアプローチです。
第5層:ペーシングとリクエストパターンの制御
どれほど完璧にブラウザを偽装しても、機械的なリクエストペースは検出されます。以下のペーシング戦略を実装してください:
- リクエスト間隔のランダム化:固定間隔ではなく、平均30秒・標準偏差10秒の正規分布でインターバルを設定。
- セッション単位のリクエスト制限:1セッションあたり50〜100リクエストでローテーション。ProxyHatの
session-フラグで新しいIPに切り替え。 - 時間帯の考慮:ターゲットサイトのトラフィックピーク時間帯にリクエストを集中させることで、異常トラフィックとしての目立ちにくを低減。
- 閲覧パターンのシミュレーション:目的ページへの直接アクセスではなく、トップページ→カテゴリページ→詳細ページという自然な閲覧経路を再現。
PerimeterXを導入している主要サイト
PerimeterX(HUMAN Security)は以下の業界で広く導入されています:
| 業界 | 代表的なサイト | 検出の厳しさ |
|---|---|---|
| 航空 | United Airlines、American Airlines、Delta Air Lines | 非常に高い(行動シグナル重視) |
| ハイエンドEC | Neiman Marcus、Saks Fifth Avenue | 高い(IPレピュテーション重視) |
| チケット販売 | 一部のチケットプラットフォーム | 非常に高い(リアルタイム対策) |
| 不動産 | 一部の不動産リスティングサイト | 中〜高い |
航空券サイトは特に厳格で、価格スクレイピング対策として行動シグナルの閾値が高く設定されています。SERPトラッキングや価格モニタリングを行う場合は、前述の5層アプローチを徹底する必要があります。
倫理的・法的フレームワーク
ボット回避技術の議論において、倫理的境界線を明確にすることが不可欠です:
- 利用規約の遵守:ターゲットサイトのToSを確認し、スクレイピングが明示的に禁止されていないか確認。禁止されている場合は、APIの利用やデータ提供元への直接交渉を優先。
- robots.txtの尊重:
robots.txtは法的拘束力がないとしても、サイト運営者の意向を示すものとして尊重すべき。 - サーバーへの配慮:適切なレート制限を実施し、ターゲットサーバーに過負荷をかけない。ピーク時を避ける、リクエスト間隔を十分に空ける。
- データの取り扱い:収集したデータの取り扱いはGDPR、CCPA等のプライバシー規制に準拠。個人データの無断収集・転売は避ける。
- セキュリティ研究の文脈:認められたペンテストやセキュリティ研究の範囲内で行う場合は、対象組織の事前同意を取得。
本稿で解説した技術は、正当な自動化・セキュリティ研究・ToSに準拠したデータ収集の文脈でのみ使用してください。スカルピング(チケット転売)や競合妨害の目的での使用は推奨しません。
ProxyHatによるPerimeterX対策の実装例
ProxyHatのレジデンシャルプロキシは、PerimeterXのIPレピュテーションチェックを通過するための基盤となります。以下は、セッション管理を含む実践的な設定例です:
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import stealth_async
import random
# ProxyHat設定 — スティッキーセッションでIP一貫性を維持
PROXY_CONFIG = {
"server": "http://gate.proxyhat.com:8080",
"username": "user-country-US-session-flight001",
"password": "YOUR_PASSWORD"
}
async def scrape_with_px_bypass(url: str):
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=False,
args=["--disable-blink-features=AutomationControlled"]
)
context = await browser.new_context(
proxy=PROXY_CONFIG,
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York",
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
)
page = await context.new_page()
await stealth_async(page)
# 自然な閲覧パターン:トップページから開始
await page.goto("https://www.example.com/", wait_until="networkidle")
await page.wait_for_timeout(random.randint(3000, 6000))
# 行動シミュレーション:スクロール + マウス移動
await human_scroll(page, random.randint(400, 800))
await page.wait_for_timeout(random.randint(1000, 3000))
# _pxhd Cookieの取得を確認
cookies = await context.cookies()
pxhd = [c for c in cookies if c["name"] == "_pxhd"]
if not pxhd:
print("警告: _pxhd Cookieが取得できませんでした")
return None
# 目的ページへ遷移
await page.click("a.nav-link", timeout=10000)
await page.wait_for_timeout(random.randint(2000, 4000))
content = await page.content()
await context.close()
await browser.close()
return content
result = asyncio.run(scrape_with_px_bypass(
"https://www.example.com/flights"
))
この実装のポイント:
session-flight001フラグでスティッキーセッションを指定。同じセッションIDの間は同じ住宅IPが維持され、_pxhdCookieのIP一貫性チェックを通過- 新しいセッションが必要な場合は、
session-flight002のようにセッションIDを変更することで新しいIPにローテーション - 都市レベルのジオターゲティングが必要な場合は、
user-country-US-city-newyorkのように指定可能
ProxyHatの料金体系や対応ロケーションの詳細は料金ページおよびロケーション一覧をご確認ください。
Key Takeaways
- PerimeterXの検出は行動シグナルが最も重い:TLSやヘッダーを完璧に偽装しても、マウスムーブメントやクリックパターンが機械的であればブロックされる。行動シミュレーションの実装が必須。
- _pxhd Cookieは一度取得すれば永続ではない:PerimeterXは全リクエストで継続的に行動シグナルを評価。Cookie取得後も自然な行動パターンを維持すること。
- IPレピュテーションは最初の関門:データセンターIPは即座にブロックされる。レジデンシャルプロキシ(住宅IP)が前提条件。
- ジオ一貫性を崩さない:IPの国、ブラウザのlocale、タイムゾーン、言語設定を全て一致させる。
- ペーシングは人間らしく:固定間隔のリクエストは最も検出されやすいパターン。ランダムな間隔と自然な閲覧経路を再現する。
- 倫理的境界線を守る:ToS遵守、サーバーへの配慮、プライバシー規制の順守は技術的対策と同じくらい重要。
まとめ
PerimeterX(HUMAN Security)の検出を通過するには、単一の対策ではなく、IPレピュテーション、ブラウザフィンガープリント、行動シグナル、TLSフィンガープリント、リクエストパターンの5層すべてに対する包括的なアプローチが必要です。中でも行動シグナルの比重が高いことがPerimeterXの最大の特徴であり、DataDomeやAkamaiとは異なる対策の優先順位を要求します。
ProxyHatのレジデンシャルプロキシは、この5層アプローチの基盤となるIPレピュテーションを確保する最も重要な要素です。Webスクレイピングのユースケースに合わせて、国・都市レベルのジオターゲティングとスティッキーセッションを活用し、PerimeterXの検出をクリーンに通過する正当な自動化フローを構築してください。






