需求描述
公司业务调整,需要对个别新域名进行简单的 CC 安全防护,需要测试 openresty 中的 waf 模块做 CC 防护。
部署测试 waf
安装 openresty
首先通过有 yum 来安装 openresty
wget https://openresty.org/package/centos/openresty.repo
mv openresty.repo /etc/yum.repos.d/
yum check-update
yum install -y openresty
安装完成后,在 nginx 中添加一个 url 测试页面
location /hello {
default_type text/html;
content_by_lua_block {
ngx.say("<p>hello, world</p>")
}
}
验证测试服务是否正常
/usr/local/openresty/nginx/sbin/nginx -t
/usr/local/openresty/nginx/sbin/nginx -s reload
部署 waf 功能模块
从 git 上下载相关文件
git clone https://github.com/unixhot/waf.git
cp -a ./waf/waf /usr/local/openresty/nginx/conf/
ln -s /usr/local/openresty/lualib/resty/ /usr/local/openresty/nginx/conf/waf/resty
/usr/local/openresty/nginx/sbin/nginx -t
/usr/local/openresty/nginx/sbin/nginx -s reload
查看文件目录
./waf/
├── access.lua
├── config.lua # 配置文件
├── init.lua # 规则函数
├── lib.lua
├── resty -> /usr/local/openresty/lualib/resty/
└── rule-config
├── args.rule
├── blackip.rule # IP黑名单
├── cookie.rule # cookie请求过滤规则
├── post.rule # post请求过滤规则
├── url.rule # url请求过滤规则
├── useragent.rule # useragent过滤规则
├── whiteip.rule # IP请求白名单
└── whiteurl.rule # url请求白名单
以上到此,可以基本实现一些常见防护攻击,但是我们这边的需求是需要根据特定的域名加上 uri 来进行限制,这个功能就无法做到,我查阅了一些资料之后根据其他文档简单的改写了一些 lua 来进行判断,如果有类似的需求可以根据下面的文档来进行操作。
waf 功能模块调整改写
这里的前提条件是需要 openresty 和 lua 等相关功能模块已经就绪可以正常使用。
我这里参考了 https://github.com/sosojustdo/tengine_waf 的文献来进行微调实现的,需要注意的是不管使用 tengine 或者是 openresty 都是可以的,我两个都测试过,只要lua的结构和函数对应进行调整即可。
首先我们来看一下这个文件目录
./conf/
└── waf
├── access.lua # 条件配置文件
├── args
├── blockip
├── config.lua # 配置文件
├── cookie
├── denycc # 基于 ip,ip+uri 等多种方式,具体可以参考上面 git 的内容介绍
├── denyhost # 域名+uri 配置文件,这个是我新增的一个文件
├── init.lua # 规则函数文件
├── post
├── resty -> /usr/local/openresty/lualib/resty/
├── url
├── user-agent
├── whiteip
└── whiteurl
这里我只介绍需要调整相关的文件内容
nginx.conf 文件
# 开启日志方便调试
error_log logs/error.log info;
# 在 http 段中增加
# WAF config
lua_shared_dict limit 50m;
lua_shared_dict blockiplimit 10m;
lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua";
init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua";
access_by_lua_file "/usr/local/openresty/nginx/conf/waf/access.lua";
access.lua 文件,在这个文件当中我新增了一个函数配置
local content_length=tonumber(ngx.req.get_headers()['content-length'])
local method=ngx.req.get_method()
local ngxmatch=ngx.re.match
if whiteip() then
elseif blockip() then
elseif denyhost() then -- 这里是我新写的一个关于域名+uri 方法的一个判断条件
elseif denycc() then
elseif ngx.var.http_Acunetix_Aspect then
say_html()
elseif whiteurl() then
elseif ua() then
elseif url() then
elseif args() then
elseif cookie() then
elseif PostCheck then
if method=="POST" then
local boundary = get_boundary()
if boundary then
local len = string.len
local sock, err = ngx.req.socket()
if not sock then
return
end
ngx.req.init_body(128 * 1024)
sock:settimeout(0)
local content_length = nil
content_length=tonumber(ngx.req.get_headers()['content-length'])
local chunk_size = 4096
if content_length < chunk_size then
chunk_size = content_length
end
local size = 0
while size < content_length do
local data, err, partial = sock:receive(chunk_size)
data = data or partial
if not data then
return
end
ngx.req.append_body(data)
if body(data) then
return true
end
size = size + len(data)
local m = ngxmatch(data,'Content-Disposition: form-data;(.+)filename="(.+)\\.(.*)"','ijo')
if m then
fileExtCheck(m[3])
filetranslate = true
else
if ngxmatch(data,"Content-Disposition:",'isjo') then
filetranslate = false
end
if filetranslate==false then
if body(data) then
return true
end
end
end
local less = content_length - size
if less < chunk_size then
chunk_size = less
end
end
ngx.req.finish_body()
else
ngx.req.read_body()
local args = ngx.req.get_post_args()
if not args then
return
end
for key, val in pairs(args) do
if type(val) == "table" or val == false then
data=table.concat(val, ", ")
else
data=val
end
if data and type(data) ~= "boolean" and body(data) then
return true
end
end
end
end
else
return
end
config.lua 文件
RulePath = "/usr/local/openresty/nginx/conf/waf/"
logdir = "/usr/local/openresty/nginx/logs/"
black_fileExt={"php","jsp"}
OnlyCheck="off"
urlMatch="on"
cookieMatch="on"
postMatch="on"
whiteurlMatch="on"
whiteipMatch="on"
blackipMatch="on"
denyhost="on" -- 这里是新增功能的开启按钮
denycc="on"
-- 这里定义被拒的提示页
html=[[
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>网站防火墙</title>
<style>
p {
line-height:20px;
}
ul{ list-style-type:none;}
li{ list-style-type:none;}
</style>
</head>
<body style=" padding:0; margin:0; font:14px/1.5 Microsoft Yahei, 宋体,sans-serif; color:#555;">
<div style="margin: 0 auto; width:1000px; padding-top:70px; overflow:hidden;">
<div style="width:600px; float:left;">
<div style=" height:40px; line-height:40px; color:#fff; font-size:16px; overflow:hidden; background:#6bb3f6; padding-left:20px;">网站防火墙 </div>
<div style="border:1px dashed #cdcece; border-top:none; font-size:14px; background:#fff; color:#555; line-height:24px; height:220px; padding:20px 20px 0 20px; overflow-y:auto;background:#f3f7f9;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; color:#fc4f03;">您的请求带有不合法参数,已被网站管理员设置拦截!</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">可能原因:您提交的内容包含危险的攻击请求或者请求次数过多</p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:1; text-indent:0px;">如何解决:</p>
<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1)检查提交内容;</li>
<li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2)请联系某某部 abc </li>
<li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3)请联系公司信息安全部;</li></ul>
</div>
</div>
</div>
</body></html>
]]
init.lua 文件,这个是自定义的规则函数,有不同的需求都可以在这个文件当中自己写代码去定义,这里我只把我自己新增的函数内容展示出来,原有的函数保持不变
-- 这里新定义个获取域名+uri 的 token 函数
function gettokenhost(data)
if data ~= nil then
return data
end
end
-- 这里定义需要屏蔽内容函数
function denyhost()
if HostDeny then
for _,rule in pairs(denyhostrules) do
if rule ~="" and string.sub(rule,1,1) ~= "#" then
local clientip=getClientIp()
local data = string.match(rule,'(.*)%s+%d+/(.*)%s+%d+')
local CCrate = string.match(rule,'.*%s+(%d+/%d+)%s+%d+')
local bantime = tonumber(string.match(rule,'.*%s+.*%s+(%d+)'))
if data ~= nil and CCrate ~=nil and bantime ~=nil then
local token=gettokenhost(data)
if token ~=nil and token == (ngx.var.host..ngx.var.request_uri) then
local CCcount=tonumber(string.match(CCrate,'(.*)/'))
local CCseconds=tonumber(string.match(CCrate,'/(.*)'))
local limit = ngx.shared.limit
local blockiplimit = ngx.shared.blockiplimit
local blockipreq,_=blockiplimit:get(clientip..data)
if blockipreq then
say_html()
return true
end
local req,_=limit:get(token)
if req then
if req >= CCcount then
log('denyhost',token,"-",rule)
blockiplimit:set(clientip..data,1,bantime)
say_html()
return true
else
limit:incr(token,1)
end
else
limit:set(token,1,CCseconds)
end
end
end
end
end
return false
end
end
定义完以上内容之后,可以去进行验证测试,我在 denyhost 文件中的定义如下
#domain/uri 10/60 60
# 这里有两个规则
# 限制 hi.test.com/hi 这个域名,在1分钟内访问超过5次之后,拒绝访问1分钟
# 限制 hi.test.com/shiyongzhi 这个域名,在1分钟内访问超过10次之后,拒绝访问1分钟
# 其他域名和 uri 则不受时间和次数限制
hi.test.com/hi 5/60 60
hi.test.com/shiyongzhi 10/60 60
网友评论