Webアプリが送信するように作られたすべてのリクエストを、配信されるJavaScriptから復元します: メソッド、URL、ボディ、コンテンツタイプ、ヘッダー。
シングルページアプリのJavaScriptバンドルには、そのアプリが送信しうるすべてのAPIリクエストが含まれています。URLを与えると、jshoundはアプリのバンドルを見つけ、遅延読み込みされるチャンクも含めてすべてをクロールし、それらのリクエストを抽出します。
これはパッシブです。送信するリクエストは、公開静的ファイル、ページ、およびそのJavaScriptに対する未認証のGETのみです。解析はローカルで実行され、アプリのAPIには何も送信されません。
git clone https://github.com/AmbitSecurity/jshound && cd jshound
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
Python 3.10以降とNode 18以降が必要です。
jshound supabase.com/dashboard
$ jshound supabase.com/dashboard
307 128B https://supabase.com/dashboard
200 19K https://supabase.com/dashboard/org
200 28K https://frontend-assets.supabase.com/studio/8617532ae1a4/_next/static/chunks/1p849pzlxcp4n.js?dpl=dpl_ESaSquAbnWrJqbvvyRh7F1qj5wWH
...
supabase.com 200 2282 files (2281 from frontend-assets.supabase.com, 1 from supabase.com) -> 387 endpoints
1 declared chunk(s) could not be fetched
-> .jshound/supabase.com/endpoints.txt (also requests.jsonl, urls.txt, openapi.json)
frontend-assets.supabase.com上の2,281ファイルも含まれ、そこにsupabaseのビルドが存在します。POST https://api.supabase.com/platform/auth/{ref}/otp {"phone":"state:phone"}
DELETE https://api.supabase.com/v1/projects/{param_n}/functions/{param_e}
DELETE https://supabase.com/dashboard/api/integrations/stripe-sync application/json {"projectRef":"input:e","startTime":"input:t"}
POST https://supabase.com/docs/api/search/fts application/json {"query":"computed:trim"}
複数のターゲットを一度に、またはstdinからリストを指定:
jshound app.example.com api.example.com
cat scope.txt | jshound
以前の実行のjs/フォルダなど、すでにディスク上にあるファイルは、ネットワークに触れることなく読み取られます:
$ jshound examples/demo-app
GET https://demo-app.test/api/feature-flags
POST https://demo-app.test/t/events {"sid":"?","evt":"?"}
DELETE https://demo-app.test/v1/sessions/current
POST {baseUrl}/v1/media multipart/form-data {"file":"file-upload"}
POST {baseUrl}/v1/subscribe application/json {"email":"computed:trim","pageUri":"state:href"}
GET {baseUrl}/v1/users/{param_o}/profile
すべては.jshound/<host>/、または-o DIRに保存されます:
endpoints.txt | 上記の表、1行につき1リクエスト |
requests.jsonl | 1リクエストにつき1つのJSONオブジェクト: method、url、body、content_type、headers、graphqlまたはrpc、およびそれが由来するページとファイル |
urls.txt | ホストが判明しているリクエストのMETHOD URL、リストを受け取るツール向け |
openapi.json | OpenAPI 3ドキュメント、Burp、Caido、Postmanにインポートするため |
js/ | 取得されたすべてのファイル |
{...}**は、jshoundが解決できなかった値で、コードが書かれているとおりに記述されます: {baseUrl}、{window.__CONFIG__.apiBaseUrl}。パス内では、{ref}はパラメータであり、{param_o}は関数パラメータoです。/api/surveys/は、jshoundがそのベースまで追跡していないコードを経由します。requests.jsonlはそれが由来するファイルを記録します。input:は引数、state:はオブジェクトのプロパティ、computed:は呼び出しの結果、?は不明。application/json?は、呼び出し箇所で読み取られたのではなく、ファイルの他のリクエストから推測されたコンテンツタイプです。~{...}**は、列挙されたものより多くのフィールドを持つボディです。{graphql}**はGraphQLオペレーションで、その名前、型、変数をボディとして持ちます。| bundler | lazy chunk URLs | module tree | how |
|---|---|---|---|
| webpack 5 | yes | yes | 構文木から__webpack_require__.uを読み取る |
| webpack 4 | yes | yes | .uを持たないため、名前付きURLビルダーは名前ではなく形状で照合される |
| Rspack | yes | yes | webpack互換で、通常はESMを出力する |
| Turbopack | yes | n/a | ビルダーがまったくなく、チャンクリストはリテラル配列 |
| Vite / Rollup | yes | n/a | パスは書き下される(__vite__mapDeps)。フラットなESMにはたどるレジストリがない |
| esbuild | yes | n/a | 同じ |
| Parcel | yes | n/a | ランタイムグローバルはビルドごとに接尾辞が付き(parcelRequire5707)、チャンクテーブルは配列ではなくオブジェクト |
| Metro (React Native) | n/a | n/a | 単一のバンドルを配信し、チャンクはない。__METRO_GLOBAL_PREFIX__で検出 |
| webpack Module Federation | yes | yes | ビルダーがidマップを持たないため、idは__webpack_require__.e(480)の呼び出し箇所から取得される |
jshoundは、指定されたターゲット、およびそのターゲットのページが読み込むJavaScriptファイルを、それらがどこでホストされていてもリクエストします。supabaseの場合、それは主にfrontend-assets.supabase.comです。これらは、どの訪問者のブラウザもダウンロードする公開静的ファイルです。それらの他のホストは、これらのGET以外には何も受け取りません。
ターゲットがプライベートアドレス空間(ループバック、RFC1918、リンクローカル)で指定したURLは拒否します。ただし、指定したターゲット自体がプライベートであるか、--allow-privateを渡した場合を除きます。
-o DIR | 結果の保存先(デフォルト.jshound/<host>) |
-q | 各リクエストが送信されるたびに表示しない |
-v | 解析ログも表示する |
--max-chunks N | この数の遅延チャンクとインポートの後に停止する(デフォルト50000) |
--no-chunks | 遅延チャンクを追跡しない |
--proxy URL | すべてのリクエストをプロキシ経由で送信する(またはJSHOUND_PROXYを設定) |
--proxy-ca FILE | TLSを傍受するプロキシのCA証明書 |
--client PROFILE | ブロックされたときにローテーションする代わりに、1つのブラウザTLSプロファイルに固定する |
--allow-private | プライベートアドレス空間のURLを許可する(スコープを参照) |
blocked by Cloudflareを報告し、何も読み取りません。jshoundはcurl_cffiが知る最新のChrome、Firefox、Safariを模倣するので、curl_cffiを最新に保ってください。--proxyはレート制限やIP禁止には役立ちますが、チャレンジには役立ちません。document.createElement("script")で挿入するスクリプトで、API呼び出しから得たURLを使用するものは、jshoundがダウンロードするどのファイルにも含まれていません。CONTRIBUTING.mdを参照してください。
MIT。