説明
オープンリダイレクトは、ユーザーが送ったURLを検証せず、redirectやsendRedirectなどの移動応答に使う場合に起こります。正規ドメインで始まるリンクに見えても、実際には外部のフィッシングサイトや悪意のあるダウンロードページへ移動する可能性があります。
ログイン後の移動先、OAuthコールバック後のreturnUrl、決済完了後のページなどで発生しやすい問題です。
想定される影響
- 信頼されるサービスのURLを使ったフィッシング
- 未検証のリダイレクトを信頼する認証処理でのトークン漏えいやセッションの窃取
- 悪意のあるファイルのダウンロードや脆弱な外部サイトへの誘導
- 許可されたドメインを経由するリダイレクトによるフィルターの回避
対処方法
- 移動先を、サーバーが管理する許可リストのパスやドメインに制限してください。
- URL全体を受け取る代わりに、
next=profileのような固定識別子をサーバーで実際のパスへ対応付けてください。 - 外部URLが必要なら、スキーム、ホスト、ポート、正規化結果を確認し、
httpsだけを許可してください。 startsWithやcontainsだけで信頼しないでください。https://trusted.example.com.evil.exampleのような値で回避されるおそれがあります。- 未許可の値は拒否するか、安全な既定パスへ移動させてください。
例
Springコントローラーの抜粋です。必要なimportとアプリケーションの内部ルート定義は省略しています。
変更前
java
@GetMapping("/login/success")
public void loginSuccess(
@RequestParam("next") String next,
HttpServletResponse response
) throws IOException {
response.sendRedirect(next);
}
変更後
java
private static final Map<String, String> ALLOWED_REDIRECTS = Map.of(
"profile", "/account/profile",
"orders", "/account/orders"
);
@GetMapping("/login/success")
public void loginSuccess(
@RequestParam(value = "next", defaultValue = "profile") String next,
HttpServletResponse response
) throws IOException {
String redirectPath = ALLOWED_REDIRECTS.getOrDefault(next, "/account/profile");
response.sendRedirect(redirectPath);
}
説明:
- 変更前: リクエストパラメーターのURLへそのまま移動します。正規サービスのリンクをクリックしても、外部サイトへ誘導される可能性があります。
- 変更後: 入力をURLではなくサーバーが把握する識別子として扱います。許可リストにない値には、安全な既定パスを使います。