説明
ユーザー入力をそのままgetRequestDispatcher()へ渡すと、内部リソースを公開するおそれがあります。ServletRequest.getRequestDispatcher()で得るディスパッチャーは、現在のWebアプリケーション内の静的・動的リソースへ要求を転送したり、その出力を取り込んだりします。直接のHTTPアクセスが禁止されたリソースにも到達し得ます。すべてのファイルが原文のまま返る、あるいはコンテキスト外のパスへアクセスできるという意味ではありません。
想定される影響
- 情報漏えい: 権限のないユーザーへ内部リソースや処理結果が公開されるおそれがあります。
- アクセス制限の回避: 転送先と適用される制御によって、意図しない内部機能へ到達する可能性があります。パスを選択できるだけで任意のJavaコードが実行されるとは限りません。
対処方法
- ユーザー入力を
RequestDispatcherへ直接渡さないでください。 - アプリケーションが管理する固定パスを優先してください。
- 許可した選択肢を固定パスへ対応付けてください。
- HTTPリダイレクトが必要な場合も、サーバーが定めた宛先だけを使用してください。
sendRedirect()は検証や認可確認の代わりにはなりません。
例
サーブレット登録と認証設定を省略しています。変更後はルートコンテキストに配置したアプリケーション内の固定ページへリダイレクトします。別のコンテキストパスでは宛先を調整し、プロフィールページでもユーザーごとの権限を確認してください。
変更前
java
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.RequestDispatcher;
public class UnsafeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String filePath = request.getParameter("file"); // ユーザーが制御する入力
RequestDispatcher dispatcher = request.getRequestDispatcher(filePath);
dispatcher.forward(request, response); // 未検証の宛先
}
}
変更後
java
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class SafeServlet extends HttpServlet {
private static final Map<String, String> allowedPaths = new HashMap<>();
static {
allowedPaths.put("home", "/home.jsp");
allowedPaths.put("profile", "/profile.jsp");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userInput = request.getParameter("page");
// 固定の対応表からページを選択
String safePath = allowedPaths.getOrDefault(userInput, "/home.jsp");
response.sendRedirect(safePath); // 固定のリダイレクト先
}
}
説明:
- 変更前:
request.getParameter("file")の値を直接ディスパッチャーへ渡すため、意図しない内部リソースの公開や呼び出しにつながる可能性があります。 - 変更後: 固定の対応表からパスを選び、ブラウザーに新しい要求を送るよう指示します。元の要求を内部で転送する動作とは異なります。