SpringBoot内嵌Tomcat防护Slow HTTP攻击实战指南
1. 项目背景与问题定位去年在给某金融系统做压力测试时我们突然发现当并发连接数达到2000左右时整个SpringBoot应用会完全停止响应。通过netstat命令查看发现有大量TCP连接卡在CLOSE_WAIT状态。这个现象让我意识到Tomcat的默认配置在面对Slow HTTP攻击时存在严重安全隐患。Slow HTTP DoS慢速HTTP拒绝服务攻击是一种利用HTTP协议特性发起的低流量攻击。攻击者通过故意缓慢发送HTTP请求头或请求体长时间占用服务器连接资源。由于Tomcat默认的最大连接数有限通常是200左右当这些慢连接占满连接池后正常用户就无法访问服务了。关键发现SpringBoot内嵌Tomcat默认配置中connectionTimeout20秒maxConnections10000但maxThreads只有200。这意味着虽然允许大量连接但实际处理能力只有200并发极易被慢连接耗尽线程池。2. 防御方案设计思路2.1 攻击原理深度解析典型的Slow HTTP攻击有三种形式Slowloris保持连接打开并缓慢发送HTTP头Slow POST声明大的Content-Length后缓慢发送bodySlow Read以极慢速度读取响应数据以Slowloris为例攻击者会GET /vulnerable-endpoint HTTP/1.1 Host: example.com User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) Content-Length: 42 X-a:然后每隔30秒发送一个X-b:这样的头字段使连接始终不完整。2.2 Tomcat防护参数矩阵通过分析Tomcat 9.x源码这些是核心防护参数参数名默认值防护作用推荐设置maxThreads200工作线程上限500-1000acceptCount100等待队列长度50-100connectionTimeout20000ms连接超时时间5000msmaxConnections10000最大并发连接数1000-2000keepAliveTimeout20000msKeep-Alive超时10000msmaxKeepAliveRequests100单个连接最大请求数50minSpareThreads10最小空闲线程502.3 防御策略组合拳我采用的综合防护方案连接限流通过maxConnections限制总连接数快速失效缩短connectionTimeout加速释放异常连接线程优化调整线程池参数匹配业务特点协议强化配置严格的keepAlive策略3. SpringBoot配置实战3.1 基础配置模板在application.yml中配置生产环境建议用application-prod.ymlserver: tomcat: threads: max: 500 min-spare: 50 connection-timeout: 5s max-connections: 1000 accept-count: 100 keep-alive: timeout: 10s max-requests: 503.2 高级防护配置对于高安全要求的场景建议添加过滤器Bean public FilterRegistrationBeanSlowHttpFilter slowHttpFilter() { FilterRegistrationBeanSlowHttpFilter registration new FilterRegistrationBean(); registration.setFilter(new SlowHttpFilter()); registration.addUrlPatterns(/*); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); return registration; } public class SlowHttpFilter implements Filter { private static final int MIN_BYTES_PER_SECOND 1024; // 1KB/s Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest (HttpServletRequest) request; CountingInputStream in new CountingInputStream(httpRequest.getInputStream()); ThrottledInputStream throttledIn new ThrottledInputStream(in, MIN_BYTES_PER_SECOND); try { chain.doFilter(new HttpServletRequestWrapper(httpRequest) { Override public ServletInputStream getInputStream() { return new ServletInputStreamWrapper(throttledIn); } }, response); } catch (ThrottleException e) { response.getWriter().write(Request too slow); ((HttpServletResponse)response).setStatus(400); } } }3.3 监控与告警配置在Prometheus中配置监控规则groups: - name: tomcat.rules rules: - alert: SlowRequestAttack expr: rate(tomcat_threads_busy_threads[1m]) 0.8 and rate(tomcat_global_request_seconds_count[1m]) 5 for: 2m labels: severity: critical annotations: summary: Possible slow HTTP attack ({{ $value }} busy threads)4. 压力测试验证4.1 测试工具配置使用slowhttptest进行攻击模拟slowhttptest -c 1000 -H -g -o slowhttp -i 10 -r 200 -t GET -u http://localhost:8080 -x 24 -p 3参数说明-c 1000建立1000个连接-i 10每10秒发送一次数据-x 24发送24字节数据-p 3每个连接持续3分钟4.2 防护效果对比测试结果对比表场景未防护QPS防护后QPS错误率CPU负载正常请求125012000.1%65%100慢连接攻击329801.2%70%500慢连接攻击服务崩溃8503.5%75%5. 生产环境注意事项线程数调优公式最佳maxThreads (平均响应时间(ms) × 目标QPS) / 1000 例如50ms响应时间2000QPS目标 → (50×2000)/1000100线程连接超时陷阱不要设置connectionTimeout 3秒否则移动网络用户会大量超时对于文件上传接口应该单独配置更长的超时KeepAlive最佳实践// 动态调整keepAliveTimeout EventListener public void onAppEvent(WebServerInitializedEvent event) { if (event.getWebServer() instanceof TomcatWebServer) { Tomcat tomcat ((TomcatWebServer) event.getWebServer()).getTomcat(); for (Connector connector : tomcat.getService().findConnectors()) { if (connector.getProtocolHandler() instanceof AbstractHttp11Protocol) { ((AbstractHttp11Protocol?) connector.getProtocolHandler()) .setKeepAliveTimeout(calculateKeepAliveTimeout()); } } } }容器化部署的特殊处理# 在Dockerfile中设置JVM参数 ENV JAVA_OPTS-Dserver.tomcat.accept-count50 \ -Dserver.tomcat.max-threads300 \ -Dserver.tomcat.connection-timeout50006. 进阶防护方案6.1 前端防护层在Nginx层添加防护http { # 限制客户端传输速度 client_body_timeout 5s; client_header_timeout 5s; # 限制请求头大小 large_client_header_buffers 4 8k; # 限制请求体大小 client_max_body_size 1m; # 限制连接频率 limit_conn_zone $binary_remote_addr zoneconn_limit:10m; limit_conn conn_limit 20; }6.2 动态调整算法实现自适应线程控制public class DynamicThreadPool extends ThreadPoolExecutor { private final int corePoolSize; private final int maximumPoolSize; public DynamicThreadPool(int corePoolSize, int maximumPoolSize) { super(corePoolSize, maximumPoolSize, 60, TimeUnit.SECONDS, new ResizableCapacityLinkedBlockingQueue(100)); this.corePoolSize corePoolSize; this.maximumPoolSize maximumPoolSize; } Scheduled(fixedRate 5000) public void adjustPoolSize() { double load getCurrentLoad(); int newSize calculatePoolSize(load); setCorePoolSize(newSize); setMaximumPoolSize(newSize); } private int calculatePoolSize(double load) { return (int) Math.min( maximumPoolSize, Math.max(corePoolSize, corePoolSize * (1 load)) ); } }6.3 机器学习防护使用TensorFlow实现异常检测import tensorflow as tf from tensorflow.keras.layers import LSTM, Dense model tf.keras.Sequential([ LSTM(64, input_shape(60, 10)), # 60个时间步10个特征 Dense(32, activationrelu), Dense(1, activationsigmoid) ]) # 特征包括请求间隔、header长度、传输速度等 model.compile(optimizeradam, lossbinary_crossentropy) model.fit(X_train, y_train, epochs10)7. 常见问题排查指南问题1调整参数后出现大量Connection reset异常检查点确认connectionTimeout keepAliveTimeout检查网络设备如负载均衡器是否有更短的超时设置用tcpdump抓包分析RST包来源问题2线程数调高后CPU负载飙升解决方案// 在SpringBoot启动类添加 Bean public TomcatConnectorCustomizer connectorCustomizer() { return connector - { ProtocolHandler handler connector.getProtocolHandler(); if (handler instanceof AbstractProtocol) { AbstractProtocol? protocol (AbstractProtocol?) handler; protocol.setMaxThreads(computeOptimalThreads()); // 限制线程创建速度 protocol.setMinSpareThreads(10); protocol.setAcceptorThreadCount(2); } }; }问题3防护配置在K8s环境中不生效根本原因容器CPU限制影响线程调度修正方法# deployment.yaml resources: limits: cpu: 2 requests: cpu: 1.5问题4防护过滤器导致文件上传失败优化方案Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (isFileUploadRequest(request)) { chain.doFilter(request, response); return; } // 原有慢速检测逻辑 }经过这些优化后我们的系统在后续的压力测试中即使面对3000的慢连接攻击仍能保持85%的正常请求处理能力。最关键的是要记住防护Slow HTTP攻击不是一次性配置而是需要持续监控和动态调整的过程。