WAF开发之防护HTTP洪水攻击
字数 1749 2025-08-29 08:29:36
HTTP洪水攻击防护技术详解
一、HTTP洪水攻击概述
HTTP洪水攻击(HTTP Flood)是一种分布式拒绝服务攻击(DDoS)形式,攻击者通过大量伪造的HTTP请求(如GET/POST)淹没目标服务器,耗尽服务器资源(包括连接数、CPU、内存等),导致正常用户无法访问服务。
攻击特点:
- 高频次、低复杂度的请求冲击:不同于CC攻击,HTTP洪水更注重通过大量简单请求造成服务瘫痪
- 模拟正常用户行为:攻击流量通常模仿合法用户请求,使得直接过滤变得困难
- 资源消耗型攻击:主要目标是耗尽服务器资源而非利用漏洞
二、HTTP洪水攻击防护策略
防护核心目标是区分正常流量与恶意流量,并动态拦截攻击源。主要防护策略包括:
1. 请求频率限制
基于IP或会话(Session)统计单位时间内的请求次数,超出阈值则触发防护动作。
2. 浏览器指纹验证
通过JavaScript收集客户端浏览器特征(如User-Agent、Canvas指纹等),验证是否为真实浏览器。
3. Cookie挑战
强制客户端执行Cookie验证流程,自动化攻击工具通常无法正确处理动态Cookie。
4. IP信誉库联动
结合实时IP黑名单或第三方威胁情报库,拦截已知恶意IP。
三、基于LUA的HTTP洪水防护实现
以下是一个基于Cookie挑战的完整防护方案实现:
1. 变量定义与初始化
local uri = ngx.var.uri -- 当前请求路径
local ip_addr = ngx.var.remote_addr -- 客户端IP
local cookie_val = ngx.var.cookie_waf_challenge or "" -- 获取客户端Cookie
local threshold = 100 -- 单位时间(如60秒)内允许的最大请求数
local challenge_expire = 300 -- Cookie有效期(秒)
2. 请求频率统计与拦截逻辑
-- 初始化共享内存(记录IP请求次数)
local http_flood = ngx.shared.http_flood
-- 统计IP请求频率
local key = "req_count:" .. ip_addr
local current_count, err = http_flood:incr(key, 1)
if current_count == nil then
http_flood:set(key, 1, 60) -- 首次计数,设置60秒过期
elseif current_count >= threshold then
-- 触发防护:生成Cookie挑战
local challenge_key = "challenge:" .. ip_addr
local challenge_code = http_flood:get(challenge_key)
if challenge_code == nil then
-- 生成随机Cookie值
local random_str = string.gsub(ngx.md5(ngx.now() .. ip_addr), "%d", "")
challenge_code = string.sub(random_str, 1, 12)
http_flood:set(challenge_key, challenge_code, challenge_expire)
end
-- 验证客户端是否携带正确Cookie
if cookie_val ~= challenge_code then
-- 返回挑战页面
return ngx.exit(ngx.HTTP_FORBIDDEN)
else
-- 验证通过,重置计数
http_flood:delete(key)
http_flood:delete(challenge_key)
end
end
3. 挑战页面实现(HTML/JS)
当请求频率超限时,返回包含JavaScript的页面,强制客户端完成Cookie验证:
if ngx.status == ngx.HTTP_FORBIDDEN then
ngx.header.content_type = "text/html"
ngx.print([[
<!DOCTYPE html>
<html>
<head>
<title>安全验证</title>
<script>
// 动态设置Cookie并重试
function setChallengeCookie() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/waf/challenge', true);
xhr.onload = function() {
if (xhr.status == 200) {
document.cookie = "waf_challenge=" + xhr.responseText + "; path=/";
location.reload(); // 重载页面以携带Cookie
}
};
xhr.send();
}
window.onload = setChallengeCookie;
</script>
</head>
<body>
<h1>正在执行安全验证,请稍候...</h1>
</body>
</html>
]])
ngx.exit(ngx.HTTP_OK)
end
4. 挑战接口实现(后端生成Cookie)
添加一个内部接口用于生成动态Cookie值:
location /waf/challenge {
internal;
content_by_lua_block {
local ip_addr = ngx.var.remote_addr
local challenge_key = "challenge:" .. ip_addr
local challenge_code = ngx.shared.http_flood:get(challenge_key)
ngx.print(challenge_code)
}
}
四、防护逻辑详解
-
请求计数机制
- 每个IP在60秒窗口期内请求超过100次时触发挑战
- 使用共享内存(ngx.shared)存储请求计数,确保多worker间数据同步
-
动态Cookie生成
- 使用MD5哈希和时间戳生成唯一Cookie值
- 移除数字增强随机性:
string.gsub(ngx.md5(ngx.now() .. ip_addr), "%d", "") - 截取前12位作为验证码:
string.sub(random_str, 1, 12)
-
客户端验证流程
- 未携带或携带错误Cookie的请求返回验证页面
- 验证页面通过JavaScript发起XHR请求获取挑战码
- 获取后设置Cookie并重载页面
-
自动重置机制
- 验证成功后清除计数器和挑战状态
- 避免误拦截正常用户后续请求
五、防护方案优化方向
-
精细化统计
- 结合URI路径、User-Agent等维度统计请求频率
- 区分静态资源和动态接口,设置不同阈值
-
浏览器指纹增强
- 集成JavaScript计算Canvas指纹
- 检测WebGL渲染能力
- 识别并拦截无头浏览器(Headless Browser)
-
IP信誉库集成
- 对接第三方威胁情报API
- 实时拦截高风险IP
- 建立本地IP信誉评分系统
-
算法优化
- 使用滑动窗口替代固定时间窗口
- 实现漏桶或令牌桶算法
- 考虑请求突发性的自适应阈值调整
-
性能优化
- 使用更高效的数据结构存储请求计数
- 实现分级缓存机制
- 优化共享内存访问锁
六、部署与调优建议
-
阈值设置原则
- 根据业务特点调整请求频率阈值
- 生产环境建议从宽松阈值开始,逐步收紧
- 区分API接口和页面请求设置不同限制
-
监控与日志
- 记录所有触发挑战的请求详情
- 监控挑战成功率,评估防护效果
- 建立报警机制,及时发现异常流量
-
用户体验优化
- 设计友好的验证页面
- 考虑CAPTCHA验证作为备选方案
- 对已验证用户提供短暂免验证期
-
压力测试
- 模拟攻击测试防护效果
- 评估防护机制对正常流量的影响
- 测试高并发下的防护性能
七、总结
本方案通过动态Cookie挑战和请求频率限制的组合,有效缓解HTTP洪水攻击:
-
技术优势
- 迫使攻击者必须执行JavaScript并维持Cookie状态
- 显著提高攻击成本
- 对正常用户影响可控
-
适用场景
- Web应用防护
- API接口保护
- 关键业务防DDoS
-
持续演进
- 结合机器学习识别异常流量
- 集成更多维度风险评估
- 实现智能自适应防护
实际部署时应根据业务特点调整参数,并通过持续监控优化防护策略,在安全防护和用户体验间取得平衡。