結論(外部URLパラメータへのオープンリダイレクト)
外部URLパラメータへのオープンリダイレクトとは、?url= や ?next= など遷移先を表すクエリパラメータに外部サイトのURLを指定すると、サーバーが検証せずにそのサイトへ転送してしまう脆弱性(CWE-601)です。自治体ドメインなど信頼された正規URLを入口に偽サイトへ誘導でき、フィッシングに直結します。遷移先はパラメータで受け取らないか、自サイト内の相対パスまたは完全一致の許可リストで検証することが対策です。
この記事のポイント
- 転送はLocationヘッダーだけでなく、Refreshヘッダー、meta refresh、JavaScriptのlocation.hrefでも起こります。
- //attacker.example や前方一致・部分一致の検証、バックスラッシュを使った回避パターンに注意が必要です。
- セキュリティAIチェッカーは遷移系パラメータを持つURLを1件選び、値を実在しない外部オリジンに置き換えて転送の有無を確認します。
- 外部オリジンへ転送すれば×、転送しなければ○、候補がなければ対象外です。
- 恒久対策はアプリ側の修正で、IDから遷移先を引く対応表方式が最も安全です。
| 診断項目名 | 外部URLパラメータへのオープンリダイレクト |
|---|---|
| 項目ID | redirect.external-parameter-probe |
| 分類 | HTTP応答・エラー処理 |
| 重大度・配点の重み | 重要(high) 重み 4 |
| 関連する標準・ガイドライン | OWASP ASVS 5.0.0 V3.7.2(L2:アプリが管理しない別ホスト・ドメインへの自動リダイレクトは、遷移先が許可リストにある場合に限る)、V3.7.3(L3:アプリ外へ遷移する際は通知と取消の機会を示す)、OWASP WSTG-CLNT-04(Testing for Client-side URL Redirect)、OWASP Unvalidated Redirects and Forwards Cheat Sheet、CWE-601(URL Redirection to Untrusted Site ('Open Redirect')) |
外部URLパラメータへのオープンリダイレクトとは
外部URLパラメータへのオープンリダイレクトとは、url、next、return_url、redirect_to など遷移先を表すクエリパラメータに外部サイトのURLを渡すと、サーバーがそれを検証せずに、その外部サイトへ利用者を転送してしまう脆弱性です(CWE-601)。
オープンリダイレクトは、正規ドメインのURLを入口にして利用者を任意の外部サイトへ誘導できてしまいます。自治体ドメイン(.lg.jp など)の信頼性を悪用したフィッシングに直結するため、セキュリティAIチェッカーでは重大度highとしています。
この項目は、サイト内のURLから遷移先を表すと思われるクエリパラメータを1件見つけ、その値を実在しない外部オリジン https://miniparse-invalid-origin.example に置き換えてアクセスし、サーバーがその外部オリジンへ転送する応答を返さないかを確認します。
オープンリダイレクトの仕組み
ログイン後に元のページへ戻す、外部リンクの利用を記録してから移動する、といった目的で、次のような処理がよく作られます。
// 脆弱な例(OWASP Cheat Sheetの例と同型)
$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);
この場合、https://www.example.lg.jp/jump.php?url=https://攻撃者のサイト/ というURLを配れば、リンクの見た目は自治体ドメインなのに、クリックした利用者は攻撃者のサイトへ移動します。
転送の方法は Location ヘッダー(301/302/303/307/308)だけではありません。Refresh レスポンスヘッダー、HTMLの <meta http-equiv="refresh" content="0;url=...">、JavaScriptの location.href = ... でも同じ問題が起こります。
オープンリダイレクトはなぜ危険で、どう回避されるのか
検証をすり抜ける典型パターン
許可リストの実装を誤ると、次のような値で回避されます。
| 回避の値 | すり抜ける検証 |
|---|---|
//attacker.example/ | スキーム省略の外部URL |
https://www.example.lg.jp.attacker.example/ | 前方一致・部分一致の検証 |
https://attacker.example/?www.example.lg.jp | 文字列を含むかだけの検証 |
https:/\attacker.example、/\attacker.example | ブラウザが \ を / として扱う挙動 |
javascript: スキーム | リダイレクトではなくリンク出力される場合、XSSにつながる |
OWASPが推奨する対策の考え方
OWASP Unvalidated Redirects and Forwards Cheat Sheetは、次の順で対策を推奨しています。
- リダイレクトをなくすことを第一とする。
- 必要な場合は、利用者の入力を許可済みURLへの対応表(短い名前やIDから遷移先を引く方式)で処理する、または許可リストで検証する。
- 外部への移動時に確認画面を出す。
| 出典 | 要求 |
|---|---|
| OWASP ASVS 5.0.0 V3.7.2(L2) | アプリが管理しない別ホスト・ドメインへの自動リダイレクトは、遷移先が許可リストにある場合に限る |
| OWASP ASVS 5.0.0 V3.7.3(L3) | アプリ外へ遷移する際は通知と取消の機会を示す |
| OWASP WSTG-CLNT-04 | クライアント側URLリダイレクトの検査 |
| CWE-601 | 信頼できないサイトへのURLリダイレクト(オープンリダイレクト) |
オープンリダイレクトについてよくある誤解
- 「自サイトのページを表示するだけなので被害はない」:被害は自サイトではなく、自治体の信頼性を利用された住民に生じます。
- 「確認画面を挟んでいる」:確認画面の「次へ」が任意URLへの移動で、遷移先がはっきり表示されていなければ同じ効果になります。ASVS V3.7.3は遷移先の通知と取消の機会を求めています。
- 「HTTPSなら安全」:オープンリダイレクトは暗号化の有無と無関係です。
外部URLパラメータへのオープンリダイレクトの診断基準(セキュリティAIチェッカー)
取得方法
- 候補URL:診断対象の最終URLと、トップページ・下層ページから抽出した同一オリジンのURL(リンク、フォーム送信先、JavaScript内のURL、V2診断の巡回候補)。
- パラメータ名が次のいずれかに完全一致(大文字小文字を区別しない)するクエリを持つ最初のURLを1件選びます:
url、uri、next、return、returnurl/return_url、returnuri/return_uri、returnto/return_to、redirect、redirecturl/redirect_url、redirecturi/redirect_uri、redirectto/redirect_to、continue、destination、dest、callback、goto。 - そのパラメータの値を
https://miniparse-invalid-origin.exampleに置き換え、GETで1回だけ送信します。リダイレクトは追跡せず、タイムアウト5秒、本文上限30KBです。外部オリジンへの実際のアクセスは発生しません。
判定条件
| 判定 | 条件 | 得点への影響 |
|---|---|---|
| × 要改善(fail) | 応答の Location が外部オリジン(https://miniparse-invalid-origin.example)を指す | 重み4 × 0 |
| × 要改善(fail) | 応答の Refresh ヘッダー、または本文中の http-equiv=refresh を持つ <meta> タグの中に、置き換えた外部ホストを指すURL(https://・http://・スキーム省略の // 形式)がある。url= の有無は問わず(0; https://… 形式も対象)、区切りの前後の空白や引用符も許容。ホスト名は完全一致で、後ろに .evil.test などが続く別ホストは不一致 | 重み4 × 0 |
| ○ 問題なし(pass) | 上記に該当しない(自サイト内への転送、エラー、通常表示など) | 重み4 × 1.00 |
| 対象外(na) | 候補パラメータを持つURLが見つからない、または置換後の応答を取得できない(タイムアウト・本文上限超過など) | 得点計算から除外 |
- 証拠欄に外部URL自体は表示しません。passの場合は応答のHTTPステータスを表示します。
- passは「候補1件・値1種類で外部転送しなかった」という意味です。
//形式や部分一致の回避パターン、JavaScriptでの転送、ログイン後のみ動く転送は検査していないため、リダイレクト機能を持つシステムでは手動確認が必要です。 redirect_uriはOAuthの認可リクエストでも使われます。認可サーバーが未登録のredirect_uriを拒否していれば、本項目はpassになります(ASVS V10.4.1は事前登録URIとの完全一致検証を求めています)。OAuthの非推奨フローの兆候は「OAuth/OIDCの非推奨フロー兆候」で確認します。
自治体サイトでの判定例
| 状況 | 判定 |
|---|---|
外部リンクのクリック計測用 /cgi-bin/jump.cgi?url=... が、渡されたURLへそのまま302 | × |
ログイン画面 /login?next=/mypage で、外部URLを指定すると / に戻される | ○ |
「外部サイトへ移動します」の中継ページが <meta http-equiv="refresh" content="3;url=..."> で外部URLへ自動遷移 | ×(許可リストでの検証が必要) |
| 遷移系パラメータを持つURLが見つからない | 対象外 |
オープンリダイレクトがあるとどんなリスクがあるか
- フィッシング:自治体ドメインのURLをメール・SMS・SNSで配布し、「給付金の申請」「税の還付」などを装った偽サイトへ誘導します。利用者はリンク先のドメインを確認しても正規ドメインしか見えません。
- フィルタ回避:メールのURLフィルタやSNSのリンク安全性チェックが、信頼済みドメインのURLとして素通しすることがあります。
- OAuthとの連鎖:認可サーバーが同一ドメイン配下へのリダイレクトを許可している場合、そのドメインのオープンリダイレクトを経由して認可コードやトークンが外部へ流出する攻撃が知られています(RFC 9700でも、リダイレクトURIの完全一致検証の理由として挙げられています)。
- XSSへの発展:遷移先がリンクや
locationにそのまま出力され、javascript:スキームを許す場合はXSSになります。
オープンリダイレクトの対策方法(PHP・Next.js・Apache・nginx)
基本方針
- 可能ならリダイレクト先をパラメータで受け取らない。
- 受け取る場合は、IDや短い名前から遷移先を引く対応表にする。
- URLを受け取る場合は、「
/で始まり//や/\で始まらない相対パス」だけを許可するか、完全一致の許可リストで検証する。 - 外部への移動は、遷移先を明示した確認画面を挟み、自動遷移させない。
PHP
<?php
function safe_redirect_path(string $value): string {
// 自サイト内の相対パスのみ許可
if ($value === '' || $value[0] !== '/' || preg_match('#^/[/\\\\]#', $value)) {
return '/';
}
if (preg_match('/[\x00-\x1F]/', $value)) return '/';
return $value;
}
header('Location: ' . safe_redirect_path($_GET['next'] ?? ''), true, 302);
exit;
外部リンク計測の場合は、URLではなくリンクIDを受け取り、サーバー側の対応表から遷移先を引きます。
$links = ['bousai' => 'https://www.jma.go.jp/', 'kenko' => 'https://www.mhlw.go.jp/'];
$id = $_GET['id'] ?? '';
if (!isset($links[$id])) { http_response_code(404); exit; }
header('Location: ' . $links[$id], true, 302);
Next.js
// lib/safe-next.ts(Route Handlerやページから呼び出す。route.ts自体には置かない)
export function safeNext(value: string | null): string {
if (!value || !value.startsWith("/") || value.startsWith("//") || value.startsWith("/\\")) return "/";
const url = new URL(value, "https://www.example.lg.jp");
return url.origin === "https://www.example.lg.jp" ? `${url.pathname}${url.search}` : "/";
}
new URL() で解釈し直して自オリジンであることを確認すると、\ や制御文字を使った回避にも強くなります。
Apache / nginx での暫定対処
アプリをすぐ直せない場合、問題のスクリプトへのアクセスで外部URLを含むパラメータを拒否します(Apacheの例はドキュメントルートの .htaccess 用です。httpd.confに書く場合はパターンを ^/cgi-bin/jump\.cgi$ にします)。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} (^|&)url=(https?%3A|https?:|%2F%2F|//) [NC]
RewriteRule ^cgi-bin/jump\.cgi$ - [F]
</IfModule>
location = /cgi-bin/jump.cgi {
if ($arg_url ~* "^(https?(:|%3A)|//|%2F%2F)") { return 403; }
# 以降は既存の設定
}
これは回避パターンを網羅できないため、恒久対策はアプリ側の修正です。
設定後の確認方法
# 外部URLを渡して Location を確認(自組織のサイトに限る)
curl -s -o /dev/null -D - 'https://www.example.lg.jp/jump.php?url=https://example.com/' | grep -iE '^(location|refresh):'
# スキーム省略・バックスラッシュの回避パターン
curl -s -o /dev/null -D - 'https://www.example.lg.jp/login?next=//example.com/' | grep -i '^location'
curl -s -o /dev/null -D - 'https://www.example.lg.jp/login?next=/%5Cexample.com/' | grep -i '^location'
# meta refresh
curl -s 'https://www.example.lg.jp/jump.php?url=https://example.com/' | grep -io '<meta[^>]*refresh[^>]*>'
サイトを壊さないための注意点
- 既存の外部リンク(バナー、関連サイト一覧など)が中継スクリプトを使っている場合、対応表方式へ移行する際にリンク切れが出ないよう、現在のリンク先を棚卸ししてください。
- ログイン後の戻り先を相対パスに限定すると、サブドメインをまたぐ連携が動かなくなることがあります。その場合は完全一致の許可リストを使います。
よくある質問(FAQ)
オープンリダイレクトは自サイトに被害がないので放置してもよいですか?
放置すべきではありません。被害は自サイトではなく、自治体の信頼性を利用された住民に生じます。正規ドメインのURLを入口にした給付金や税の還付を装うフィッシングに悪用されます。
「外部サイトへ移動します」という確認画面を挟めば安全ですか?
確認画面の「次へ」が任意URLへの移動で、遷移先がはっきり表示されていなければ同じ効果になります。ASVS V3.7.3は遷移先の通知と取消の機会を求めています。確認画面からmeta refreshで外部へ自動遷移する場合は、セキュリティAIチェッカーでも×になります。
診断で○でしたが、オープンリダイレクトは無いと言えますか?
言い切れません。○は「候補1件・値1種類で外部転送しなかった」という意味です。//形式や部分一致の回避パターン、JavaScriptでの転送、ログイン後のみ動く転送は検査していないため、リダイレクト機能を持つシステムでは手動確認が必要です。
オープンリダイレクトの診断では、どのパラメータ名が対象になりますか?
url、uri、next、return、return_url、return_uri、return_to、redirect、redirect_url、redirect_uri、redirect_to(各アンダースコアなし表記も含む)、continue、destination、dest、callback、gotoです。大文字小文字を区別せず完全一致で判定します。
OAuthのredirect_uriも対象になりますか?
対象になります。認可サーバーが未登録のredirect_uriを拒否していれば○です。ASVS V10.4.1は事前登録URIとの完全一致検証を求めています。
すぐにアプリを直せない場合の暫定対処はありますか?
Apacheのmod_rewriteやnginxで、問題のスクリプトへのアクセスのうち外部URLを含むパラメータを403で拒否する方法があります。ただし回避パターンを網羅できないため、恒久対策はアプリ側の修正です。
この項目を含む127項目を、URLを入れるだけで無料診断します。