最終更新:2026-01-18
※生成AIに解説させています。真偽に関してはよく確認の上自己責任でお読みください。
概要
Webサイトの移転やメンテナンスにおいて、クライアントサイド・リダイレクト(JavaScript等を用いた転送)は有用な手段である。しかし、その実装方法を誤ると、攻撃者に悪用される「オープンリダイレクト(Open Redirect)」脆弱性を生むリスクがある。本稿では、攻撃者がリダイレクトを狙う理由と、安全性を担保する実装コードの差異について解説する。
1. 攻撃者の視点:なぜリダイレクトが悪用されるか
攻撃者にとって、GitHub Pages(*.github.io)のような「社会的信頼の高いドメイン」は、攻撃の成功率を高めるための踏み台として極めて高い価値を持つ。その主な理由は以下の2点に集約される。
信頼の悪用(Authority Exploitation)
ユーザーはURLを確認した際、「GitHubのドメインであれば安全だろう」というヒューリスティックな判断(油断)を行いやすい。攻撃者はこの心理を突き、信頼できるドメインからフィッシングサイト(例:偽のログイン画面)へ密かに転送させることで、ユーザーに疑念を抱かせることなく認証情報を窃取する。
フィルタの回避(Filter Evasion)
メールフィルタやアンチウイルスソフトは、ブラックリストに基づいて既知の悪性URLをブロックする。しかし、入り口となるURLが「無害で正当なサイト」であれば、これらのセキュリティフィルタを通過し、エンドユーザーの端末まで到達してしまう可能性が高まる。
2. 脆弱な実装と安全な実装の比較
リダイレクトの安全性を分ける決定的な要因は、転送先(Destination)が「外部入力に依存している」か、それとも「管理者が固定(ハードコード)している」かにある。
▼ 危険な実装例:オープンリダイレクト
以下は、URLパラメータから転送先を動的に取得するJavaScriptの例である。
// 【危険】URLパラメータで指定された場所に無条件で飛ばす実装
const params = new URLSearchParams(window.location.search);
const target = params.get("url"); // 例: ?url=http://example.com
if (target) {
// 検証なしに外部入力値をそのまま転送先に指定している
window.location.replace(target);
}
この実装が存在する場合、攻撃者は以下のようなリンクを作成・頒布することが可能となる。
https://trusted-domain.com/?url=http://example.com
このリンクを踏んだユーザーは、信頼された trusted-domain.com を経由して、即座に攻撃者が用意した example.com へと強制転送される。これがオープンリダイレクト脆弱性である。
▼ 安全な実装例:ドメインの固定 以下は、転送先のドメイン部分を文字列として固定(ハードコード)した実装例である。
// 【安全】行き先は特定のドメインに固定されている実装
const newDomain = "https://ss0832-github-io.pages.dev";
// パスやパラメータは維持しつつ、ドメイン部分のみを強制的に指定する
window.location.replace(newDomain + window.location.pathname + window.location.search);
このコードでは、window.location.replace の引数において、ドメイン部分(https://ss0832-github-io.pages.dev )が文字列結合によって固定されている。
仮に攻撃者が ?query=http://example.com のようなパラメータを付加したとしても、それは単なるクエリ文字列として扱われるに過ぎず、ブラウザは必ず管理者によって意図された新ドメイン上のパスへ遷移する。
3. 高度な悪用事例
単なるフィッシングサイトへの誘導以外にも、オープンリダイレクトはより深刻な攻撃の「起点」として利用されるケースがある。
事例1:OAuth認証トークンの窃取
現代のWebサービスで多用されるOAuth 2.0認証フローにおいて、オープンリダイレクトは致命的な脆弱性となる。
- 攻撃のシナリオ:
- 攻撃者は、正規の認証プロバイダ(例:GoogleやFacebook)のログインURLを作成する。この時、
redirect_uriパラメータに「脆弱性のある正規サイト」を指定する。 https://provider.com/auth?redirect_uri=https://trusted-site.com/open-redirect?url=http://attacker.com- ユーザーが正規プロバイダでログインを許可すると、認証コード(Auth Code)やアクセストークンが付与され、
redirect_uriへ転送される。 trusted-site.comのオープンリダイレクト機能が作動し、アクセストークンを持ったまま攻撃者のサイトattacker.comへ再転送される。
- 攻撃者は、正規の認証プロバイダ(例:GoogleやFacebook)のログインURLを作成する。この時、
- 結果: 攻撃者はユーザーのアクセストークンを入手し、アカウントを乗っ取ることが可能になる。
コメント:GitHub PagesでOAuthを使う場面がなかった(そもそも使えるかどうかも知らない)ので関係ないが、こうしたものもあるということで記憶しておくと良いかもしれない。
事例2:DOM-based XSSへの昇格
JavaScriptでリダイレクトを処理している場合、URLスキームの検証が不十分だと、リダイレクト脆弱性がクロスサイトスクリプティング(XSS)へと昇格する。
- メカニズム:
攻撃者が転送先として
http://...ではなくjavascript:alert(1)のようなJavaScriptスキームを指定する。// 脆弱なコード const target = params.get("url"); window.location.replace(target); // ここに "javascript:..." が渡るとスクリプトが実行される - 結果: ユーザーのブラウザ上で任意のスクリプトが実行され、Cookie(セッションID)の窃取や、偽の入力フォームの表示が行われる。
事例3:セキュリティフィルタの回避(Redirect Chain)
マルウェア配布やフィッシングにおいて、セキュリティ製品の検知を逃れるためにリダイレクトが悪用される。
- メカニズム:
メールフィルタやURLスキャナは、リンク先の安全性評価を行うが、リダイレクトの追跡回数には制限(例:3回まで)がある場合が多い。攻撃者は複数の正規サイトのオープンリダイレクトを数珠繋ぎ(チェーン)にすることで、スキャナの追跡を振り切ってから悪性サイトへ到達させる。
GitHub (Clean)→Bitly (Clean)→Vulnerable Site (Clean)→Malware Site (Detection evaded) - 結果: 静的なブラックリストや簡易的なクローラでは脅威を検知できず、ユーザーの手元まで悪性リンクが到達する。
4. 防御策の深化
前述の「ドメインの固定(ハードコード)」に加え、以下の対策を組み合わせることで堅牢性はさらに向上する。
- プロトコル検証: 入力値を受け取る場合でも、
http:またはhttps:で始まるもの以外(javascript:やdata:)を厳格に拒否する。 - クッションページの設置: 外部サイトへ遷移する際に「外部サイトへ移動します」という中間ページを挟み、ユーザーにクリック(明示的な意思確認)を求めることで、自動的な悪用チェーンを断ち切る。
結論
リダイレクト機能を実装する際は、外部からの入力値(クエリパラメータ等)を信用せず、転送先のドメインをサーバーサイドまたはクライアントサイドのコード内で厳密に固定することが、セキュリティ上の必須要件である。