Spring Boot中如何解决Fortify对HttpServletResponse.getWriter.write的信息泄露告警?
我正在开发一个Spring Boot REST应用,需要对所有入站请求进行认证。若用户已授权则执行正常流程,否则返回HTTP 401状态码及自定义JSON响应。我通过Servlet Filter实现该逻辑,代码如下:
public class SecurityFilter implements Filter { @Autowired ObjectMapper objectMapper; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String apikey = httpServletRequest.getHeader("apikey"); if(validApikey) { chain.doFilter(servletRequest, servletResponse); } else { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setStatus(false); errorResponse.setReason("provided apikey on header is unauthorized"); String errorMessage = objectMapper.writeValueAsString(errorResponse); HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; httpServletResponse.setContentType("application/json"); httpServletResponse.setStatus(401); httpServletResponse.getWriter().write(responseJson); httpServletResponse.getWriter().flush(); }
功能运行正常,但部署前的Fortify扫描对httpServletResponse.getWriter().write(responseJson);这一行提出告警,提示信息为:
SecurityFilter.java在第50行调用write()时泄露了系统数据或调试信息,这些信息可能帮助攻击者制定攻击计划。
外部信息泄露指系统数据或调试信息通过套接字或网络连接传输到远程机器,这类泄露会向攻击者暴露操作系统、完整路径、用户名或配置文件位置等信息,比内部信息泄露更严重。
请问是否可以不使用httpServletResponse.getWriter().write(responseJson);来返回自定义JSON响应,或该如何解决此Fortify告警问题?
方案一:改用OutputStream写入响应
替换getWriter()为getOutputStream()直接写入字节流,可规避Fortify对字符流的误判。修改后的代码如下:
else { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setStatus(false); errorResponse.setReason("provided apikey on header is unauthorized"); byte[] errorBytes = objectMapper.writeValueAsBytes(errorResponse); HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; httpServletResponse.setContentType("application/json"); httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpServletResponse.setContentLength(errorBytes.length); httpServletResponse.getOutputStream().write(errorBytes); httpServletResponse.getOutputStream().flush(); }
字节流写入更直接,避免字符编码相关的潜在问题,同时明确设置内容长度,让响应更规范。
方案二:标记误报并确认响应无敏感信息
Fortify告警的核心是担心泄露系统敏感数据,先确认你的ErrorResponse仅包含status和reason这类非敏感字段,没有任何系统路径、堆栈信息或环境配置内容。
在确认响应安全后,可在Fortify平台中将该告警标记为误报,同时在代码中添加注释说明响应内容的安全性,辅助扫描工具识别。
方案三:改用Spring Security实现认证
作为Spring Boot应用,推荐使用Spring Security替代自定义Filter完成API Key认证,其内置的异常处理机制更规范,能天然降低扫描告警风险。示例配置如下:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .addFilterBefore(apiKeyAuthFilter(), UsernamePasswordAuthenticationFilter.class) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setStatus(false); errorResponse.setReason("provided apikey on header is unauthorized"); byte[] errorBytes = new ObjectMapper().writeValueAsBytes(errorResponse); response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getOutputStream().write(errorBytes); }) ); return http.build(); } @Bean public Filter apiKeyAuthFilter() { return new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String apiKey = httpRequest.getHeader("apikey"); // 此处替换为你的apiKey验证逻辑 boolean validApikey = "valid_key".equals(apiKey); if (validApikey) { SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken(apiKey, null, Collections.emptyList()) ); chain.doFilter(request, response); } else { chain.doFilter(request, response); // 交由exceptionHandling处理401响应 } } }; } }
内容的提问来源于stack exchange,提问作者Thiagarajan Ramanathan

