説明
例外発生時の内部スタックトレースをクライアントへそのまま返すと、クラスやパッケージの構造、ファイルパス、メソッドの呼び出し順序、フレームワークやライブラリのバージョンを推測できる情報が漏れるおそれがあります。攻撃者は境界値などの入力で意図的に例外を起こし、収集した情報をSQLインジェクションやパストラバーサルなどの攻撃に利用できます。
想定される影響
- 内部構造の露出: クラス名、呼び出し経路、ファイルシステムのパスが、防御を回避する手掛かりになります。
- フレームワークやバージョンの特定: 使用中のライブラリを推測され、既知のCVEを狙った攻撃につながるおそれがあります。
- 攻撃の具体化: エラー箇所や入力の流れから、SQLインジェクション、リモートコード実行、パス操作などの攻撃を組み立てやすくなります。
- 設定情報の漏えい: ホスト名、設定値、開発環境と本番環境の違いがメッセージに含まれる場合があります。
対処方法
- クライアントには「リクエストを処理できません」などの一般的なメッセージを返し、スタックトレースを含めないでください。
- 詳細は
logger.error("message", ex)などでサーバーログにのみ記録し、アクセス制御と保存期間を設定してください。 - Servlet Filter、Springの
@ControllerAdvice、JAX-RSのExceptionMapperなど、共通の例外処理層で応答を統一してください。 - 本番環境では詳細エラーを無効にしてください。例えばSpring Bootでは
server.error.include-stacktrace=neverを設定し、独自のエラーページを用意します。 - ユーザー向けメッセージに、クラス名、パス、SQL、設定値が含まれていないか確認してください。
例
変更前
java
import javax.servlet.http.*;
import java.io.IOException;
public class ErrorEchoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String v = req.getParameter("div");
if (v != null) {
int n = Integer.parseInt(v);
int r = 100 / n; // 0 입력 시 예외 발생 가능
resp.getWriter().println("result=" + r);
}
} catch (Exception ex) {
// BAD: 스택 트레이스를 사용자 응답에 직접 출력
ex.printStackTrace(resp.getWriter());
}
}
}
変更後
java
import javax.servlet.http.*;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.*;
public class ErrorSafeServlet extends HttpServlet {
private static final Logger LOG = Logger.getLogger(ErrorSafeServlet.class.getName());
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String v = req.getParameter("div");
if (v != null) {
int n = Integer.parseInt(v);
int r = 100 / n;
resp.getWriter().println("ok");
return;
}
resp.getWriter().println("ok");
} catch (Exception ex) {
String errorId = UUID.randomUUID().toString();
// 서버 로그에만 상세 스택 트레이스 기록
LOG.log(Level.SEVERE, "Unhandled exception, errorId=" + errorId, ex);
// 사용자에게는 일반화된 오류 메시지 전송
resp.resetBuffer();
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "요청 처리 중 오류가 발생했습니다. (id=" + errorId + ")");
}
}
}
解説:
- 変更前:
ex.printStackTrace(resp.getWriter())がスタックトレースをHTTP応答へ書き込み、内部の名前、パス、呼び出しの流れを公開します。 - 変更後: スタックトレースはサーバーのログに記録します。クライアントには一般的なメッセージと内部情報を含まない相関IDを返し、運用担当者がログを追跡できるようにします。