Spring ModelAndViewを通じたファイルの公開

Spring ModelAndViewを通じたファイルの公開

説明

Spring MVCのModelAndViewに指定したビュー名は、構成されたViewResolverによって実際のビューへ解決されます。常にJSPファイルへ対応するわけではありません。未検証の入力で名前を決めると、構成によっては意図しない内部ビューやリダイレクト・フォワード先を選ばれる可能性があります。

想定される影響

  • 情報漏えい: 必要な認可を確認していないビューから機密データが公開されるおそれがあります。
  • 意図しない処理: 利用可能なビューとリゾルバーの設定によって、内部処理や外部への移動が発生する可能性があります。ビュー名を制御できるだけで、任意のJavaコードが実行されるとは限りません。

対処方法

  • ユーザー入力をModelAndViewのビュー名として直接渡さないでください。
  • アプリケーションが管理する固定のビュー名を優先してください。
  • 許可した選択肢を固定ビューへ対応付けてください。
  • リダイレクトが必要な場合も、サーバーが定義した宛先だけを使い、移動先のアクセス権限を確認してください。sendRedirect()は入力検証の代わりにはなりません。

例

ビューリゾルバーと認証の設定を省略した抜粋です。homeとprofileはアプリケーションで定義する例の名前であり、プロフィール情報には別途ユーザーごとの認可が必要です。

変更前

java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;

@Controller
public class UnsafeController {

    @GetMapping("/view")
    public ModelAndView unsafeView(HttpServletRequest request) {
        String viewName = request.getParameter("page"); // ユーザーが制御する入力
        return new ModelAndView(viewName); // 未検証の宛先
    }
}

変更後

java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Controller
public class SafeController {

    private static final Map<String, String> allowedViews = new HashMap<>();

    static {
        allowedViews.put("home", "home");
        allowedViews.put("profile", "profile");
    }

    @GetMapping("/view")
    public ModelAndView safeView(@RequestParam(name = "page", required = false, defaultValue = "home") String page) {
        // 固定のビュー対応表から選択
        String safeView = allowedViews.getOrDefault(page, "home");
        return new ModelAndView(safeView);
    }

    @GetMapping("/redirect")
    public void safeRedirect(@RequestParam(name = "page", required = false, defaultValue = "home") String page,
                             HttpServletResponse response) throws IOException {
        String redirectPath = allowedViews.getOrDefault(page, "/home");
        response.sendRedirect(redirectPath); // 固定のリダイレクト先
    }
}

説明:

  • 変更前: pageパラメーターをModelAndViewのビュー名として直接使用します。
  • 変更後: 固定の対応表からビュー名を選び、不明な値はhomeに置き換えます。別のリダイレクトメソッドも固定の宛先を使います。相対URLが実際のデプロイ先のパスで正しく解決されることを確認してください。

関連CVE

参考資料