如何在Spring应用中实现Length hiding以防御BREACH攻击?
在Spring应用中实现长度隐藏(Length Hiding)缓解BREACH攻击
BREACH攻击利用压缩后的响应长度变化窃取敏感信息,长度隐藏通过向响应末尾添加随机数字节干扰攻击者的长度分析,是有效的缓解手段之一。以下是Spring应用中的具体实现方案:
方法一:自定义Servlet Filter拦截并修改响应
1. 实现响应包装类
创建HttpServletResponseWrapper子类,用于捕获响应内容并添加随机字节:
import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponseWrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Random; public class LengthHidingResponseWrapper extends HttpServletResponseWrapper { private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); private PrintWriter writer; private final Random random = new Random(); public LengthHidingResponseWrapper(HttpServletResponse response) { super(response); } @Override public ServletOutputStream getOutputStream() throws IOException { return new ServletOutputStream() { @Override public void write(int b) throws IOException { buffer.write(b); } @Override public boolean isReady() { return true; } @Override public void setWriteListener(WriteListener writeListener) { // 无需额外实现 } }; } @Override public PrintWriter getWriter() throws IOException { if (writer == null) { writer = new PrintWriter(buffer); } return writer; } @Override public void flushBuffer() throws IOException { // 生成1-100个随机字节,可根据需求调整范围 int randomByteCount = random.nextInt(100) + 1; byte[] randomBytes = new byte[randomByteCount]; random.nextBytes(randomBytes); buffer.write(randomBytes); // 将修改后的内容写入原始响应 ServletOutputStream out = getResponse().getOutputStream(); out.write(buffer.toByteArray()); out.flush(); buffer.reset(); } }
2. 实现自定义Filter
创建OncePerRequestFilter子类,拦截请求并应用响应包装:
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; public class LengthHidingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 仅对文本类型响应生效,避免破坏二进制内容(如图片、文件) String contentType = response.getContentType(); if (contentType != null && (contentType.contains("text/html") || contentType.contains("application/json"))) { LengthHidingResponseWrapper wrappedResponse = new LengthHidingResponseWrapper(response); filterChain.doFilter(request, wrappedResponse); wrappedResponse.flushBuffer(); } else { filterChain.doFilter(request, response); } } }
3. 注册Filter到Spring容器
在Spring Boot中通过配置类完成注册:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class LengthHidingConfig { @Bean public FilterRegistrationBean<LengthHidingFilter> lengthHidingFilter() { FilterRegistrationBean<LengthHidingFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new LengthHidingFilter()); // 拦截所有路径,可根据业务调整 registrationBean.addUrlPatterns("/*"); // 设置优先级,确保在响应压缩等过滤器之前执行 registrationBean.setOrder(1); return registrationBean; } }
关键注意事项
- 避免破坏二进制内容:必须判断响应的
Content-Type,仅对HTML、JSON等文本类型添加随机字节,否则会导致图片、文件下载等内容损坏。 - 压缩兼容性:如果应用启用了响应压缩(如Gzip),需确保Length Hiding Filter在压缩过滤器之前执行,否则随机字节会被压缩,无法起到干扰长度的作用;也可直接禁用响应压缩作为替代方案。
- 随机字节范围:随机字节数量不宜过大(避免影响响应性能)或过小(无法有效干扰攻击者),推荐1-100字节的范围。
内容的提问来源于stack exchange,提问作者NVJ
相关产品推荐
相关产品推荐

