基于ngx_lua模块的WAF开发实践

安全 应用安全
WAF主要分为硬件WAF和软件防火墙,硬件WAF如绿盟的NSFOCUS Web Application Firewall,软件防火墙比较有名的是ModSecurity,再就是代码级别的ngx_lua_waf。

0x00 常见WAF简单分析

WAF主要分为硬件WAF和软件防火墙,硬件WAF如绿盟的NSFOCUS Web Application Firewall,软件防火墙比较有名的是ModSecurity,再就是代码级别的ngx_lua_waf。下面谈谈个人对几款防火墙的理解:

硬件WAF个人觉得只适合在那种访问量较少的网站,比如政府网站,公司的介绍网站等等。硬件WAF的的优势在于规则有专门的安全公司维护,管理方便,但也存在一个致命的弱点,使用传统的方式来解包到应用层对性能的需求较高,而且当访问量很大的时候延时比较大,这样在高并发访问的情况下要使用硬件WAF就只能使用很多台WAF了,这样成本就非常高了;还有一个在接触过程中发现的问题,就是硬件WAF的规则虽然多而且有人维护,但是一般公司很难敢直接开启阻难,很多都是只记录,并不能阻难,这样WAF的意义就变得小多了。

ModSecurity在网上的评价都是很高的,性能高,规则全。最开始我研究的也是这款WAF,但是在实际使用过程中发现问题,就是在高并发的情况下,运行一段时间,会出现内存飙升,而且不下来的问题。这个问题再ModSecurity的讨论论坛上面也发现了有人提出这样的问题,但一直未解决(https://github.com/SpiderLabs/ModSecurity/issues/785)。针对于规则全的优势,一般使用者也不敢直接开启所有的规则拦截,毕竟每个公司的业务不同,规则也不可能直接套用。

基于高性能,低成本的想法,发现了@loveshell开发的ngx_lua_waf,经过实际使用下来,确实性能极好,由于LUA语言的性能是接近于C的,而且ngx_lua_module本身就是基于为nginx开发的高性能的模块。安全宝的云 WAF,以及cloudflare的新waf也是基于此模块使用LUA开发的。结合ModSecurity的思路,参考@loveshell的ngx_lua_waf来开发适合自己用的WAF,其中使用了很多@loveshell的函数,再此也表示感谢。

0x01 WAF框架设计

WAF开发过程中的主要方向为:

◆主引擎的开发,主要关注主引擎的性能和容错能力
◆规则的开发,主要关注规则的全面可靠,防勿拦截以及防绕过
◆整体方案能够适应多站点,高可用性的环境

WAF的主要功能为:

◆ip黑白名单
◆url黑白名单
◆useragent黑白名单
◆referer黑白名单
◆常见web漏洞防护,如xss,sql注入等
◆cc攻击防护
◆扫描器简单防护
◆其他你想要的功能

WAF的总体检测思路:

◆当用户访问到nginx时,waf首先获取用户的ip,uri,referer,useragent,,cookie,args,post,method,header信息。
◆将获取到的信息依次传给上述功能的函数,如ip规则,在ip规则中,循环到所有的ip规则,如果匹配到ip则根据规则的处理方式来进行处理,匹配到之后不继续匹配后续规则。
◆需要开启的功能依次在主函数中调用即可,顺序也可根据实际场景来确定最合适的顺序。

图示如下:

基于ngx_lua模块的WAF开发实践 #p#

0x02 规则格式分析

规则说明:

比如规则:{"rule00001","rules","args|post|cookie",[[../]],"deny","logon"},

rule00001:规则编号,随意写

rules:规则名称,如xssrules,随意写

args|post|cookie|header:检测位置,|表示或,args,post,cookie,header可多选

../:匹配的正则表达式,标准PCRE正则

deny:处理方式,可选deny ,allow

logon:日志记录与否,可选logon,logoff

0x03 cc攻击防护代码示例

  1. --在nginx.conf的HTTP中加入  
  2. --lua_shared_dict limit 50m; 根据主机内存调合适的值  
  3. --lua_shared_dict iplimit 20m;  
  4. --lua_shared_dict blockiplimit 5m;  
  5. -------------------------------------------------------------  
  6. CCDeny="on"   --cc攻击开关  
  7. CCrate="60/60"--基于url的计数 次/秒  
  8. ipCCrate="600/60"--基于ip的计数 次/秒  
  9. -------------------------------------------------  
  10. ccdenyrules={"ccdeny1","ccdeny","","","","logon"}  
  11. function gethost()  
  12.     host = ngx.var.host  
  13.     if host == nil or type(host) ~= "string" then  
  14.         math.randomseed(os.time())  
  15.         host = "nohost"..math.random()  
  16.     end  
  17.     return host  
  18. end  
  19.    
  20. function denycc(clientdata)  
  21.     if CCDeny=="on" then  
  22.         local uri=clientdata[2]  
  23.         local host = gethost()  
  24.         CCcount=tonumber(string.match(CCrate,'(.*)/'))  
  25.         CCseconds=tonumber(string.match(CCrate,'/(.*)'))  
  26.         ipCCcount=tonumber(string.match(ipCCrate,'(.*)/'))  
  27.         ipCCseconds=tonumber(string.match(ipCCrate,'/(.*)'))  
  28.         local token = clientdata[1]..host..uri  
  29.         local clientip = clientdata[1]..host  
  30.         local limit = ngx.shared.limit  
  31.         local iplimit = ngx.shared.iplimit  
  32.         local blockiplimit = ngx.shared.blockiplimit  
  33.         local req,_=limit:get(token)  
  34.         local ipreq,_=iplimit:get(clientip)  
  35.         local blockipreq,_=blockiplimit:get(clientip)  
  36.         if blockipreq or ipreq then  
  37.             if blockipreq or req then  
  38.                 if blockipreq or req >= CCcount or ipreq >= ipCCcount  then  
  39.                     log(ccdenyrules,clientdata)  
  40.                     blockiplimit:set(clientip,1,300)  
  41.                     ngx.exit(403)  
  42.                     return true  
  43.                 else  
  44.                     limit:incr(token,1)  
  45.                     iplimit:incr(clientip,1)  
  46.                 end  
  47.             else  
  48.                 limit:set(token,1,CCseconds)  
  49.             end  
  50.         else  
  51.             iplimit:set(clientip,1,ipCCseconds)  
  52.         end  
  53.     end  
  54.     return false  
  55. end 

#p#

0x04 优势举例

可以很灵活的实现复杂的控制

比如我在我的个人网站上面就使用了这样一个功能,后台页面需要特定useragent才能访问。

代码如下:

  1. --特定页面容许特定useragent可访问  
  2. function houtai(clientdata)  
  3.     if stringmatch(clientdata[2],"wp-admin") then  
  4.         if stringmatch(clientdata[4],"hahahaha") then  
  5.             return  
  6.         else  
  7.             ngx.exit(403)  
  8.             return  
  9.         end  
  10.     else  
  11.         return  
  12.     end  
  13. end 

可以测试http://www.zhangsan.me/wp-admin/

只有在特定的useragent才可以访问此页面,否则报403错误。

0x05 源码下载及使用

源码下载地址为:http://pan.baidu.com/s/18QQya

环境搭建就参考:http://wiki.nginx.org/HttpLuaModule#Installation

waf使用主要就是配置config.lua

SecRuleEngine = "on" attacklog = "on" logpath = "/home/waflog/"

分别为引擎是否开启 是否记录日志 日志的存储路径 日志的存储路径需要给予nginx运行用户的读写权限

0x06 后续研究方向

1.根据ModSecurity规则提取一份较适应自己用的规则

2.根据最新出现的漏洞维护规则

3.在多个站点的情况下,如果在站点变动,规则变动的时候,不影响其他站点,实现高可用性。

写的很简单,大牛勿喷,希望大家多提建议。

0x07 参考资料

1. https://github.com/loveshell/ngx_lua_waf

2. http://wiki.nginx.org/HttpLuaModule

3. http://www.freebuf.com/tools/54221.html

……

责任编辑:蓝雨泪 来源: 乌云知识库
相关推荐

2013-07-17 17:03:23

Ngx_luaNginx

2015-05-15 10:36:43

ngx_lua_waf多站点

2018-06-13 07:06:05

2022-07-29 09:35:25

WAF溯源识别

2009-01-03 14:57:19

ibmdwLotusWeb2.0

2013-12-03 17:28:56

Lua脚本语言

2023-08-16 19:14:02

微前端

2022-06-27 09:36:29

携程度假GraphQL多端开发

2011-08-31 16:06:50

LuaWeb开发

2015-07-30 10:46:50

2021-05-10 09:40:29

鸿蒙HarmonyOS应用

2011-08-29 15:53:04

Lua位运算

2015-07-17 10:25:43

kubernetesDocker集群系统

2022-08-08 07:05:36

KubeSphere分级管理

2011-08-24 14:26:08

Lua游戏脚本

2020-10-23 13:46:45

ATT安全能力

2011-07-08 10:57:25

Lua

2011-09-06 17:45:14

LUA开发环境Decoda

2013-06-03 10:02:53

WAF绕过

2012-11-23 16:30:16

点赞
收藏

51CTO技术栈公众号