Kasadaのアンチボットプラットフォームに直面したシニアエンジニアの多くは、最初のリクエストで429を受け取り、原因がIPなのかブラウザ指紋なのかTLSなのか、どこから手を付けるべきか迷います。Kasadaアンチボット解説として、本記事ではips.jsチャレンジの内部構造からTLS指紋、IPレピュテーションスコアリングまで、検出メカニズム全体を技術的に分解し、正規の自動化シナリオでクリーンに通過するための実装アプローチを示します。対象読者はシニアスクレイピングエンジニアやアンチボット研究者です。
Kasadaアンチボット解説:検出の全体像
KasadaはクライアントサイドのJavaScriptチャレンジを中核とするボット検出プラットフォームで、2026年現在、大手Eコマース、航空会社、チケット販売、金融サービスで広く採用されています。検出は単一の信号ではなく、複数レイヤーの重み付けスコアリングで構成されています:
- レイヤー1(事前):IPレピュテーション、ASN分類、TLS指紋(JA3/JA4)、HTTP/2 SETTINGSフレーム指紋
- レイヤー2(チャレンジ):ips.jsの実行、バイトコードVMによるデバイス・ブラウザ指紋の収集
- レイヤー3(検証):サーバー側での暗号化ペイロード検証、KP_UIDz Cookieの発行、x-kpsdk-* ヘッダーのチェック
各レイヤーは直列に実行され、いずれかで失敗すると429ステータスコードが返されます。重要なのは、レイヤー1で不合格となった場合、レイヤー2のJavaScriptチャレンジすら配信されないことです。これが、データセンタープロキシを使ったリクエストがips.jsに到達する前にブロックされる理由です。
ips.jsチャレンジの内部構造
バイトコードVMとエンコードされた文字列テーブル
Kasadaのコアチャレンジスクリプトであるips.jsは、約449KBのカスタムバイトコード仮想マシン(VM)として配信されます。このファイルは通常のJavaScriptではなく、独自のオペコードセットを定義したVM上で動作するバイトコードを含んでいます。主要な特徴は以下の通りです:
- エンコードされた文字列テーブル:APIエンドポイント名、DOM API名、プロパティ名などがXORまたはカスタムエンコーディングで難読化され、実行時に復号されます。これにより静的解析が困難になります。
- 時間ベースのシード:VMの実行はタイムスタンプに依存するシード値を使用し、リプレイ攻撃を防ぎます。同じペイロードを再送しても、数秒後には無効になります。
- 整合性チェックサム:スクリプト自身のハッシュを計算し、改ざんを検出します。DevToolsでブレークポイントを設定したり、コードを修正したりすると、チェックサムが不一致となりチャレンジが失敗します。
このVMアーキテクチャにより、Kasadaは従来の「ヘッドレスブラウザ検出」をはるかに超えた検出を行います。VM内部で収集される信号には、navigator.webdriverのような単純なフラグだけでなく、JavaScriptエンジンの内部挙動(エラーメッセージのフォーマット、Function.prototype.toStringの出力、performance.now()の精度)が含まれます。
KP_UIDz Cookieとx-kpsdk-ctヘッダーファミリー
ips.jsの実行が成功すると、KasadaのサーバーはKP_UIDzというCookieを発行します。このCookieは暗号化されたデバイス指紋とセッション状態を含み、後続リクエストでの「信頼済みクライアント」の証明として機能します。KP_UIDzが存在しない、または無効な場合、サーバーは再チャレンジを要求します。
リクエスト時には以下のヘッダーファミリーが付与されます:
| ヘッダー | 役割 | 失敗時の挙動 |
|---|---|---|
x-kpsdk-ct | チャレンジトークン(暗号化ペイロード)。ips.jsの実行結果を含む。 | 無効・期限切れの場合、429を返す。レスポンスにもx-kpsdk-ctが含まれ、エラー詳細を示す。 |
x-kpsdk-cd | クライアントデータ。デバイス指紋のハッシュ等を含む。 | 不一致の場合、再チャレンジ要求。 |
x-kpsdk-dv | デバイス検証値。VM実行の整合性を示す。 | 改ざん検出時、即座に429。 |
429レスポンスにx-kpsdk-ctが含まれる場合、これはトークン検証が失敗したことを意味します。具体的には、ips.jsが正しく実行されなかったか、生成されたペイロードがサーバー側の期待と一致しなかったか、時間ベースのシードが期限切れになったかのいずれかです。このヘッダーをデバッグの第一指標として使用してください。
VMによるブラウザ・デバイス指紋の収集
ips.jsのVMは実行時に、数十のブラウザ・デバイス信号を収集し、それらを暗号化してローテーションするペイロードとして送信します。収集される主な信号は以下の通りです:
ブラウザ指紋信号
- Canvas指紋:
HTMLCanvasElement.toDataURL()の出力ハッシュ。GPU、ドライバー、フォントレンダリングエンジンの差異を検出します。 - WebGL指紋:
WEBGL_debug_renderer_info拡張からベンダー・レンダラー文字列を取得。ヘッドレス環境のGPUエミュレーションを検出。 - AudioContext指紋:
OfflineAudioContextで生成した音声バッファのハッシュ。 - フォント列挙:インストール済みフォントのリストを間接的に推測。
- 画面・ウィンドウプロパティ:
screen.width、screen.height、window.devicePixelRatio、window.innerWidth/innerHeightの組み合わせ。
行動・環境信号
- マウス・タッチイベント:移動軌跡の曲率、クリック間隔の分布。直線的な移動や均一な間隔は自動化を示唆。
- タイミング分析:
performance.now()の分解能と、イベント間の時間分布。 - JSエンジン内部挙動:
Function.prototype.toString()の出力、エラースタックトレースのフォーマット、Error.prepareStackTraceの存在確認。
これらの信号は暗号化され、リクエストごとに異なるペイロードとして送信されます。暗号化キーは時間ベースのシードから派生するため、同じペイロードの再利用はできません。これは、Kasada bypassを試みる際に「一度取得したトークンを再利用する」アプローチが機能しない理由です。
TLS(JA3/JA4)とHTTP/2指紋+IPレピュテーション
JavaScriptチャレンジの前に、Kasadaはネットワークレイヤーで強力な事前フィルタリングを行います。これらはips.jsが配信される前に実行されるため、ここで不合格となるとチャレンジの機会すら得られません。
TLS指紋(JA3/JA4)
TLSハンドシェイクのClientHelloメッセージには、クライアントがサポートする暗号スイートのリスト、拡張機能、楕円曲線群が含まれます。この順序と組み合わせは、ブラウザやHTTPライブラリごとに固有の「指紋」を形成します。
JA3はTLS 1.2以前のClientHelloの指紋を、JA4はTLS 1.3を含むより詳細な指紋を生成します。例えば、Chrome 120のClientHelloは、16個の暗号スイートを特定の順序で提示しますが、Pythonのrequestsライブラリが使用するOpenSSLデフォルトは異なる順序・異なるセットを提示します。Kasadaはこの差異を検出し、ブラウザを装った非ブラウザクライアントを即座にブロックします。
JA3/JA4の概念については、SalesforceのJA3プロジェクトで詳細が公開されています。
HTTP/2指紋
HTTP/2では、SETTINGSフレームのパラメータ値、WINDOW_UPDATEのサイズ、HEADERSフレームの擬似ヘッダー順序が、クライアント固有の指紋を形成します。AkamaiのHTTP/2 fingerprinting研究やMDNのHTTP/2ドキュメントで説明されているように、HTTP/2の実装詳細はライブラリごとに異なります。Kasadaはこれらを組み合わせて、HTTPライブラリ(axios、requests、httpx)と実際のブラウザを区別します。
IPレピュテーションスコアリング
KasadaのIPレピュテーションシステムは、リクエスト元IPのASN、地理的一貫性、過去のボット活動履歴をスコアリングします。最も重要なのはASN分類です:
- データセンターASN(AWS、Google Cloud、Azure、DigitalOcean等):信頼スコアが著しく低く設定され、多くの場合JSチャレンジ配信前にブロックされます。
- レジデンシャルISP ASN(Comcast、AT&T、NTT、Deutsche Telekom等):高い信頼スコア。JSチャレンジが配信され、適切に実行されれば通過可能。
- モバイルキャリアASN(Verizon Wireless、SoftBank等):中〜高信頼スコア。
IPレピュテーションはKasadaの検出において最大の重み付けを持つ信号の一つです。どれだけ完璧なブラウザ指紋を提示しても、データセンターIPからアクセスすれば、レイヤー1でブロックされる可能性が高いです。
なぜレジデンシャルプロキシが必須なのか
KasadaのASNベースの事前フィルタリングにより、データセンタープロキシからのリクエストは多くの場合、ips.jsチャレンジに到達する前に拒否されます。これは「ブラウザ指紋を完璧にすれば通過できる」という前提がKasadaに対しては成り立たないことを意味します。
| プロキシタイプ | Kasada通過可能性 | 理由 | 適した用途 |
|---|---|---|---|
| データセンター | 極めて低い | ASNが事前ブロックリストに含まれる。JSチャレンジすら配信されない。 | Kasada保護対象外のターゲット |
| レジデンシャル | 高い(ブラウザ指紋が適切なら) | ISPのASNを使用。IPレピュテーションが高く、JSチャレンジが配信される。 | Kasada保護サイトの正規スクレイピング |
| モバイル | 中〜高 | キャリアASNを使用するが、一部キャリアIPは高ボット比率でフラグ付けされる場合あり。 | モバイルユーザー挙動のエミュレート |
レジデンシャルプロキシは、ISPから割り当てられた実際の家庭用IPアドレスをルーティングに使用するため、KasadaのASN分類で「一般ユーザー」として扱われます。これにより、レイヤー1の事前フィルタリングを通過し、ips.jsチャレンジを受信・実行する機会が得られます。
ProxyHatのレジデンシャルプロキシネットワークは、多数の国と都市にまたがるISPベースのIP出口を提供し、Kasadaの地理的一貫性チェック(IPの国とブラウザのnavigator.languageの一致)にも対応できます。
ProxyHatを使った正規通過の実装
Kasadaをクリーンに通過するための鍵は、「実際のブラウザランタイムでips.jsを実行させ、有効なKP_UIDzを発行させる」ことです。これは、正規の自動化(公開データの監視、認可されたテスト)において正当なアプローチです。
アーキテクチャの概要
- ProxyHatレジデンシャルプロキシを経由してブラウザランタイム(Playwright/Puppeteer)を起動
- ターゲットサイトにアクセスし、ips.jsがブラウザ内で自然に実行される
- KP_UIDz Cookieが発行された後、セッション内で後続リクエストを行う
- スティッキーセッションでIPを固定し、IP変更による再チャレンジを防ぐ
Python + Playwright + SOCKS5の実装例
from playwright.sync_api import sync_playwright
import time
# ProxyHat SOCKS5 residential proxy with sticky session
proxy_config = {
"server": "socks5://gate.proxyhat.com:1080",
"username": "user-country-US-session-kasada01",
"password": "pass"
}
with sync_playwright() as p:
browser = p.chromium.launch(
proxy=proxy_config,
headless=False, # Kasada detects headless mode
args=[
"--disable-blink-features=AutomationControlled",
"--no-first-run",
"--no-default-browser-check"
]
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York"
)
page = context.new_page()
# Navigate — ips.js executes in the real browser context
page.goto("https://target-site.com", wait_until="networkidle", timeout=60000)
# Wait for KP_UIDz cookie to be set
time.sleep(3)
cookies = context.cookies()
kp_uid = [c for c in cookies if c["name"] == "KP_UIDz"]
if kp_uid:
print(f"KP_UIDz acquired: {kp_uid[0]['value'][:20]}...")
# Now make authenticated API calls within the same session
response = page.evaluate("""
async () => {
const res = await fetch('/api/data', {
credentials: 'include'
});
return res.status;
}
""")
print(f"API response: {response}")
else:
print("KP_UIDz not set — check x-kpsdk-ct in response headers")
browser.close()
curlを使った基本的なプロキシ接続テスト
# Test residential proxy connectivity (HTTP)
curl -x http://user-country-US:pass@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
-H "Accept-Language: en-US,en;q=0.9" \
-v https://target-site.com 2>&1 | grep -E "x-kpsdk|HTTP/|KP_UIDz"
スティッキーセッションの重要性
KasadaはIPアドレスとKP_UIDz Cookieを関連付けて検証します。リクエストごとにIPが変更されると、Cookieの有効性が失われ、再チャレンジが要求されます。ProxyHatのスティッキーセッション機能(user-session-XXXX)を使用すると、同一セッションIDの間は同じ出口IPが維持されます:
# Sticky session — same IP for the session duration
http://user-country-US-session-mytask01:pass@gate.proxyhat.com:8080
これにより、IPローテーションによる再チャレンジのオーバーヘッドを回避できます。詳細な設定についてはProxyHatドキュメントを参照してください。
よくあるミスとエッジケース
1. ヘッドレスモードの使用
PlaywrightやPuppeteerのデフォルトのheadless=Trueは、KasadaのVMが検出する最も明白な信号の一つです。navigator.webdriverフラグの設定、--headlessフラグによるChrome固有の挙動の差異、DevToolsプロトコルの痕跡などが検出されます。必ずheadless=Falseで実行するか、Xvfb等の仮想ディスプレイを使用してください。
2. TLS指紋の不一致
実際のブラウザを使用していても、プロキシ接続の方法によってTLS指紋が変化することがあります。特に、HTTP CONNECTトンネルを経由する場合、プロキシソフトウェアがTLSを終端して再暗号化すると、ブラウザ本来のJA3/JA4指紋が失われます。SOCKS5プロキシを使用し、ブラウザが直接TLSハンドシェイクを行う構成にしてください。
3. 地理的一貫性の欠如
米国のIPアドレスでアクセスしながら、ブラウザのnavigator.languageがja-JPに設定されている場合、Kasadaはこの矛盾をフラグ付けします。プロキシの地理的位置とブラウザのロケール・タイムゾーンを一致させてください:
# US exit IP + US locale/timezone
http://user-country-US-session-task01:pass@gate.proxyhat.com:8080
# Browser: locale="en-US", timezone="America/New_York"
# Germany exit IP + German locale/timezone
http://user-country-DE-session-task02:pass@gate.proxyhat.com:8080
# Browser: locale="de-DE", timezone="Europe/Berlin"
4. x-kpsdk-ctの期限切れ
KP_UIDzとx-kpsdk-ctトークンには有効期限があります。長時間実行されるスクレイピングジョブでは、定期的にブラウザコンテキストをリフレッシュし、新しいチャレンジを実行する必要があります。経験上、トークンの有効期間は約30〜60分程度であり、これを超えると429が返されます。
5. 並行リクエストの過多
同一IPから短時間に多数のリクエストを送信すると、Kasadaのレート制限が発動します。1IPあたりのリクエストレートを実際のユーザーの挙動に近づけることが重要です。ProxyHatの複数セッション機能を活用して、リクエストを分散させてください:
# Distribute across sessions
http://user-country-US-session-worker01:pass@gate.proxyhat.com:8080
http://user-country-US-session-worker02:pass@gate.proxyhat.com:8080
http://user-country-US-session-worker03:pass@gate.proxyhat.com:8080
適切な利用範囲と法的留意点
本記事で説明する技術は、認可されたテスト、公開データの監視、セキュリティ研究など正当な目的にのみ使用してください。以下の用途には使用しないでください:
- 不正購入(スニーカーボット、チケットスキャルピング等の規約違反)
- クレデンシャルスタッフィングやアカウント乗っ取り
- ターゲットサイトの利用規約(ToS)に明示的に違反するデータ収集
米国ではComputer Fraud and Abuse Act(CFAA)が不正アクセスを規制しており、EUではGDPRが個人データの処理に制限を設けています。スクレイピング対象が公開データであっても、ターゲットサイトのToSとrobots.txtを確認し、適切なレート制限を設けることが法的リスクを軽減する基本原則です。
ProxyHatはプロキシインフラストラクチャを提供するものであり、ユーザーの利用目的に対する法的助言は行いません。利用前に法務担当者に相談してください。
Key Takeaways
- Kasadaは多層防御:IP ASN → TLS/HTTP2指紋 → ips.js VMチャレンジの3レイヤーで構成。いずれかで失敗すると429。
- データセンターIPは機能しない:ASNベースの事前ブロックにより、JSチャレンジすら配信されない。レジデンシャルプロキシが必須。
- ips.jsは実際のブラウザで実行する:バイトコードVM、時間ベースシード、整合性チェックサムにより、トークンの再利用や静的解析は不可能。Playwright等で自然に実行させる。
- x-kpsdk-ctが429の原因を示す:レスポンスの
x-kpsdk-ctヘッダーを確認し、トークン検証の失敗をデバッグする。- スティッキーセッションでIPを固定:IP変更による再チャレンジを防ぐため、同一セッション内で同一出口IPを維持する。
- 地理的一貫性を保つ:プロキシの国とブラウザのロケール・タイムゾーンを一致させる。
- 正規の目的のみ:認可されたテスト、公開データ監視、セキュリティ研究に限定し、ToSとCFAA/GDPRを遵守する。
Kasadaの検出メカニズムを理解し、適切なプロキシインフラと実ブラウザランタイムを組み合わせることで、正規の自動化プロジェクトは高い成功率で通過できます。ProxyHatのレジデンシャルプロキシは柔軟なプランで提供されており、Webスクレイピング用途やSERP追跡にも対応しています。まずは小規模なテストから始め、成功率とレイテンシを測定しながらスケールすることを推奨します。






