通过nginx+lua或openresty实现web站点的waf功能

WAF简介

 什么是WAF

  Web应用防护系统(也称:网站应用级入侵防御系统 。英文:Web Application Firewall,简称: WAF)。利用国际上公认的一种说法:Web应用 防火墙 是通过执行一系列针对HTTP/HTTPS的 安全策略 来专门为Web应用提供保护的一款产品。

 WAF的功能

  支持IP白名单和黑名单功能,直接将黑名单的IP访问拒绝。
  支持URL白名单,将不需要过滤的URL进行定义。
  支持User-Agent的过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  支持CC攻击防护,单个URL指定时间的访问次数,超过设定值,直接返回403。
  支持Cookie过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  支持URL过滤,匹配自定义规则中的条目,如果用户请求的URL包含这些,返回403。
  支持URL参数过滤,原理同上。
  支持日志记录,将所有拒绝的操作,记录到日志中去

 WAF的特点

 1、异常检测协议

  Web应用防火墙会对HTTP的请求进行异常检测,拒绝不符合HTTP标准的请求。并且,它也可以只允许HTTP协议的部分选项通过,从而减少攻击的影响范围。甚至,一些Web应用防火墙还可以严格限定HTTP协议中那些过于松散或未被完全制定的选项。

 2、增强的输入验证 

  增强输入验证,可以有效防止网页篡改、信息泄露、木马植入等恶意网络入侵行为。从而减小Web服务器被攻击的可能性。

 3、及时补丁 

  修补Web安全漏洞,是Web应用开发者最头痛的问题,没人会知道下一秒有什么样的漏洞出现,会为Web应用带来什么样的危害。WAF可以为我们做这项工作了——只要有全面的漏洞信息WAF能在不到一个小时的时间内屏蔽掉这个漏洞。当然,这种屏蔽掉漏洞的方式不是非常完美的,并且没有安装对应的补丁本身就是一种安全威胁,但我们在没有选择的情况下,任何保护措施都比没有保护措施更好。

 4、基于规则的保护和基于异常的保护 

  基于规则的保护可以提供各种Web应用的安全规则,WAF生产商会维护这个规则库,并时时为其更新。用户可以按照这些规则对应用进行全方面检测。还有的产品可以基于合法应用数据建立模型,并以此为依据判断应用数据的异常。但这需要对用户企业的应用具有十分透彻的了解才可能做到,可现实中这是十分困难的一件事情。

 5、状态管理 

  WAF能够判断用户是否是第一次访问并且将请求重定向到默认登录页面并且记录事件。通过检测用户的整个操作行为我们可以更容易识别攻击。状态管理模式还能检测出异常事件(比如登陆失败),并且在达到极限值时进行处理。这对暴力攻击的识别和响应是十分有利的。

 6、其他防护技术 

  WAF还有一些安全增强的功能,可以用来解决WEB程序员过分信任输入数据带来的问题。比如:隐藏表单域保护、抗入侵规避技术、响应监视和信息泄露保护。

 7、WAF相对于网络防火墙的区别

  网络防火墙作为访问控制设备,主要工作在OSI模型三、四层,基于IP报文进行检测。只是对端口做限制,对TCP协议做封堵。其产品设计无需理解HTTP会话,也就决定了无法理解Web应用程序语言如HTML、SQL语言。因此,它不可能对HTTP通讯进行输入验证或攻击规则分析。针对Web网站的恶意攻击绝大部分都将封装为HTTP请求,从80或443端口顺利通过防火墙检测。

  一些定位比较综合、提供丰富功能的防火墙,也具备一定程度的应用层防御能力,如能根据TCP会话异常性及攻击特征阻止网络层的攻击,通过IP分拆和组合也能判断是否有攻击隐藏在多个数据包中,但从根本上说他仍然无法理解HTTP会话,难以应对如SQL注入、跨站脚本、cookie窃取、网页篡改等应用层攻击。

  web应用防火墙能在应用层理解分析HTTP会话,因此能有效的防止各类应用层攻击,同时他向下兼容,具备网络防火墙的功能。

 部署WAF

 1、WAF实现规划

  分析步骤如下:解析HTTP请求==》匹配规则==》防御动作==》记录日志 

  具体实现如下:1、解析http请求:协议解析模块  2.、匹配规则:规则检测模块,匹配规则库 3、防御动作:return 403 或者跳转到自定义界面 4、日志记录:记录到elk中,画出饼图,建议使用json格式

 2、基于lua模块部署nginx

  至于nginx的安装部署请参考:https://www.cnblogs.com/goser/articles/7550582.html,本文只做nginx基于lua模块的编译安装

  下载当前最新的lua及时编译器luajit和nginx开发工具NDK(ngx_devel_kit),以及lua模块lua-nginx-module

[root@linux-node2 ~]# cd  /tools/
[root@linux-node2 tools]# wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz
[root@linux-node2 tools]# wget https://github.com/simplresty/ngx_devel_kit/archive/v0.3.0.tar.gz
[root@linux-node2 tools]# wget https://github.com/openresty/lua-nginx-module/archive/v0.10.10.tar.gz
[root@linux-node2 tools]# ll
-rw-r--r--  1 root root  849845 Aug 27 11:28 LuaJIT-2.0.5.tar.gz
-rw-r--r--  1 root root  611973 Aug 27 13:29 lua-nginx-module-0.10.10.tar.gz
-rw-r--r--  1 root root   66455 Aug 27 11:40 ngx_devel_kit-0.3.0.tar.gz

  安装LuaJIT

[root@linux-node2 tools]# tar  xf LuaJIT-2.0.5.tar.gz  
[root@linux-node2 tools]# cd  LuaJIT-2.0.5
[root@linux-node2 LuaJIT-2.0.5]# make  && make  install

  解压NDK和lua-nginx-module,并将解压包放到nginx部署路径下

[root@linux-node2 tools]# tar  xf ngx_devel_kit-0.3.0.tar.gz 
[root@linux-node2 tools]# tar  xf  lua-nginx-module-0.10.10.tar.gz 
#创建存放NDK和lua模块的路径,将这些模块放到这个路径下
mkdir -p  /application/nginx/extra/modules
[root@linux-node2 tools]# cp  -r ngx_devel_kit-0.3.0  /application/nginx/extra/modules/ngx_devel_kit
[root@linux-node2 tools]# cp  -r lua-nginx-module-0.10.10  /application/nginx/extra/modules/lua-nginx-module

  创建luajit环境变量

[root@linux-node2 LuaJIT-2.0.5]# export LUAJIT_LIB=/usr/local/lib
[root@linux-node2 LuaJIT-2.0.5]# export LUAJIT_INC=/usr/local/include/luajit-2.0

  重新编译nginx,将lua模块添加进去

[root@linux-node2 tools]# cd  nginx-1.12.2
[root@linux-node2 nginx-1.12.2]#  ./configure --prefix=/application/nginx-1.12.2 --user=www --group=www --with-http_stub_status_module --with-http_ssl_module --add-module=/application/nginx/extra/modules/ngx_devel_kit/  --add-module=/application/nginx/extra/modules/lua-nginx-module/  --with-file-aio --with-http_dav_module
#停止nginx服务,重新部署nginx
[root@linux-node2 nginx-1.12.2]# /application/nginx/sbin/nginx  -s  stop
[root@linux-node2 nginx-1.12.2]# make  -j2  && make  install    # -j2表示同时使用两个任务

  检查nginx语法报语法错误,解决如下:

[root@linux-node2 ~]# /application/nginx/sbin/nginx  -t
/application/nginx/sbin/nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
#上面报错 ,说明nginx没有加载到lua库,调整lua库文件地址
[root@linux-node2 ~]# ln  -s  /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
#启动nginx服务
[root@linux-node2 ~]# /application/nginx/sbin/nginx

  安装完毕后,下面可以测试安装了,修改nginx.conf 增加第一个配置

#部分代码修改如下,在www.conf配置文件中添加如下location段
[root@linux-node2 conf]# vim  extra/www.conf 
        location /hello {
        default_type 'text/plain';
        content_by_lua 'ngx.say("hello,lua,welcome!!!")';
        }

#重载nginx使配置生效
[root@linux-node2 conf]# /application/nginx/sbin/nginx  -t
[root@linux-node2 conf]# /application/nginx/sbin/nginx  -s reload

  使用浏览器打开http://192.168.80.172/hello检查lua模块是否加载

  部署基于lua的waf应用:这里使用开源的waf项目https://github.com/loveshell/ngx_lua_waf

[root@linux-node3 ~]# git  clone  https://github.com/loveshell/ngx_lua_waf.git
#将waf规则库放到ngixn的配置文件路径下
[root@linux-node3 ~]# mv ngx_lua_waf  /application/nginx/conf/waf
[root@linux-node3 ~]# cd  /application/nginx/conf/waf/
#修改waf的配置文件,修改规则库规则的对应路径并设置日志记录位置
[root@linux-node3 waf]# vim config.lua 
RulePath = "/application/nginx/conf/waf/wafconf/"
attacklog = "on"
logdir = "/tmp/"
UrlDeny="on"
Redirect="on"
CookieMatch="on"
postMatch="on" 
whiteModule="on" 
black_fileExt={"php","jsp"}
ipWhitelist={"127.0.0.1"}
ipBlocklist={"1.0.0.1"}
CCDeny="on"
CCrate="30/60"
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)如网站托管,请联系空间提供商;</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>
]]
#修改ngxin.conf配置文件,在http段中添加如下内容
[root@linux-node3 conf]# vim  nginx.conf
....
    lua_shared_dict limit 10m;
    lua_package_path "/application/nginx/conf/waf/?.lua";
    init_by_lua_file  /application/nginx/conf/waf/init.lua;
    access_by_lua_file /application/nginx/conf/waf/waf.lua;
....
#重载nginx服务
[root@linux-node3 conf]# /application/nginx/sbin/nginx  -t
[root@linux-node3 conf]# /application/nginx/sbin/nginx  -s  reload

  测试waf配置是否生效,使用http://192.168.80.173/mysql.sql模拟sql注入操作。显示效果如下:

  对应的日志记录如下:

[root@linux-node3 conf]# tail  -f  /tmp/www.goser.com_2018-08-31_sec.log 
192.168.201.211 [2018-08-31 13:56:31] "GET www.goser.com/mysql.sql" "-"  "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" "\.(bak|inc|old|mdb|sql|backup|java|class)$"

  测试客户端使用压力测试工具对网站攻击

[root@linux-node2 tools]# yum  install  httpd-tools -y
[root@linux-node2 tools]# ab   -c  50  -n 1000  http://192.168.80.173/
。。。。
Concurrency Level:      50
Time taken for tests:   0.707 seconds
Complete requests:      1000
Failed requests:        941
。。。。
#通过上面的ab测压工具可以看到失败了941次客户端请求,这是由config.lua配置文件决定的
。。。。
CCDeny="on"
CCrate="30/60"
。。。。

使用openresty部署WAF

  我们都知道Nginx有很多的特性和好处,但是在Nginx上开发成了一个难题,Nginx模块需要用C开发,而且必须符合一系列复杂的规则,最重要的用C开发模块必须要熟悉Nginx的源代码,使得开发者对其望而生畏。为了开发人员方便,所以接下来我们要介绍一种整合了Nginx和lua的框架,那就是OpenResty,它帮我们实现了可以用lua的规范开发,实现各种业务,并且帮我们弄清楚各个模块的编译顺序。关于OpenResty,我想大家应该不再陌生,随着系统架构的不断升级、优化,OpenResty在被广泛的应用。

  Nginx 采用的是 master-worker 模型,一个 master 进程管理多个 worker 进程,基本的事件处理都是放在 woker 中,master 负责一些全局初始化,以及对 worker 的管理。在OpenResty中,每个 woker 使用一个 LuaVM,当请求被分配到 woker 时,将在这个 LuaVM 里创建一个 coroutine(协程)。协程之间数据隔离,每个协程具有独立的全局变量_G。

  ps. 协程和多线程下的线程类似:有自己的堆栈,自己的局部变量,有自己的指令指针,但是和其他协程程序共享全局变量等信息。线程和协程的主要不同在于:多处理器的情况下,概念上来说多线程是同时运行多个线程,而协程是通过代码来完成协程的切换,任何时刻只有一个协程程序在运行。并且这个在运行的协程只有明确被要求挂起时才会被挂起。

  首先我们选择使用OpenResty,其是由Nginx核心加很多第三方模块组成,其最大的亮点是默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。

  借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序。

  而且OpenResty提供了大量组件如Mysql、Redis、Memcached等等,使在Nginx上开发Web应用更方便更简单。目前在京东如实时价格、秒杀、动态服务、单品页、列表页等都在使用Nginx+Lua架构,其他公司如淘宝、去哪儿网等。

  下载openresty并编译安装openresty

[root@linux-node2 tools]# wget  https://openresty.org/download/openresty-1.13.6.1.tar.gz
[root@linux-node2 tools]# tar  xf  openresty-1.13.6.1.tar.gz 
[root@linux-node2 tools]# cd openresty-1.13.6.1
[root@linux-node2 openresty-1.13.6.1]# ./configure  --prefix=/application/openresty-1.13.6.1   --with-luajit --with-pcre --with-pcre-jit --user=www --group=www --with-http_stub_status_module --with-http_ssl_module 
[root@linux-node2 openresty-1.13.6.1]# gmake && gmake install

[root@linux-node2 openresty-1.13.6.1]# ln -s /application/openresty-1.13.6.1  /application/openresty
#停止原生的nginx服务,启动openresty中的nginx服务
[root@linux-node2 openresty-1.13.6.1]# /application/nginx/sbin/nginx  -s  stop
[root@linux-node2 openresty-1.13.6.1]# /application/openresty/nginx/sbin/nginx  -t
[root@linux-node2 openresty-1.13.6.1]# /application/openresty/nginx/sbin/nginx 

  测试openresty安装

#修改nginx.conf文件,添加如下location段
[root@linux-node2 tools]# cd  /application/openresty/nginx/conf/
[root@linux-node2 conf]# vim  nginx.conf
        location /hi {
        default_type text/html;
        content_by_lua_block{
         ngx.say("hello openrastry!!!");
        }
        }
#重载openresty中的nginx服务
[root@linux-node2 conf]# /application/openresty/nginx/sbin/nginx -s  reload

  打开http://192.168.80.172/hi

 基于openresty来部署WAF

#在github上克隆下代码
[root@linux-node2 ~]# git clone https://github.com/unixhot/waf.git
#将waf文件拷贝到openresty的nginx路径下
[root@linux-node2 ~]# cp  -r  waf/waf/   /application/openresty/nginx/conf/
#修改waf中的config.lua文件,修改lua防火前规则路径
[root@linux-node2 ~]# cd  /application/openresty/nginx/conf/
[root@linux-node2 conf]# vim waf/config.lua 
--WAF config file,enable = "on",disable = "off"

--waf status
config_waf_enable = "on"
--log dir
config_log_dir = "/tmp/waf_logs"
--rule setting
config_rule_dir = "/application/openresty/nginx/conf/waf/rule-config"
--enable/disable white url
config_white_url_check = "on"
--enable/disable white ip
config_white_ip_check = "on"
--enable/disable block ip
config_black_ip_check = "on"
--enable/disable url filtering
config_url_check = "on"
--enalbe/disable url args filtering
config_url_args_check = "on"
--enable/disable user agent filtering
config_user_agent_check = "on"
--enable/disable cookie deny filtering
config_cookie_check = "on"
--enable/disable cc filtering
config_cc_check = "on"
--cc rate the xxx of xxx seconds
config_cc_rate = "30/60"
--enable/disable post filtering
config_post_check = "on"
--config waf output redirect/html
config_waf_output = "html"
--if config_waf_output ,setting url
config_waf_redirect_url = "https://www.cnblogs.com/goser"
config_output_html=[[
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-cn" />
<title>网站防火墙</title>
</head>
<body>
<h1 align="center"> 您的行为已违反本网站相关规定,注意操作规范。</h1>
</body>
</html>
]]
#修改openresty中nginx的配置文件,在http段添加如下内容:
[root@linux-node2 conf]# vim  nginx.conf
    lua_shared_dict limit 50m; #防cc使用字典,大小50M
    lua_package_path "/application/openresty/nginx/conf/waf/?.lua";
    init_by_lua_file "/application/openresty/nginx/conf/waf/init.lua";
    access_by_lua_file "/application/openresty/nginx/conf/waf/access.lua";
#根据日志记录位置,创建日志目录并授予www用户的权限
[root@linux-node2 conf]# mkdir /tmp/waf_logs
[root@linux-node2 conf]# chown   www.www  /tmp/waf_logs/
#检查nginx语法并重载nginx服务
[root@linux-node2 conf]# /application/openresty/nginx/sbin/nginx  -t
[root@linux-node2 conf]# /application/openresty/nginx/sbin/nginx  -s reload

  至于lua语言的编写可以参考Nginx API for Lua  :https://github.com/openresty/lua-nginx-module#nginx-api-for-lua

  模拟sql注入即url攻击

  这时候日志显示如下,记录了UA,匹配规则,URL,客户端类型,攻击的类型,请求的数据

[root@linux-node2 conf]# tail  -f  /tmp/waf_logs/2018-08-30_waf.log 
{"local_time":"2018-08-30 11:52:23","client_ip":"192.168.80.253","rule_tag":"\\.(bak|inc|old|mdb|sql|backup|java|class|tgz|gz|tar|zip)$","attack_method":"Deny_URL","server_name":"192.168.80.172","req_url":"\/mysql.sql","req_data":"-","user_agent":"Mozilla\/5.0 (Windows NT 5.1) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/49.0.2623.112 Safari\/537.36"}

  使用一台客户端,使用ab压测工具模拟防cc攻击

#安装ab测压工具
[root@linux-node3 ~]# yum  install httpd-tools  -y
[root@linux-node3 ~]# ab  -c  50  -n  1000  http://192.168.80.172/
....
Concurrency Level:      50
Time taken for tests:   0.220 seconds
Complete requests:      1000
Failed requests:        979
......
#从上面可以看出失败了979此请求,这是由config.lua配置文件配置决定的
[root@linux-node2 waf]# vim  config.lua 
--cc rate the xxx of xxx seconds
config_cc_rate = "30/60"

  当然还可以模拟ip黑名单和模拟ip白名单,配置类似,只要在/application/openresty/nginx/conf/waf/rule-config/whiteip.rule或blackip.rule 规则文件添加白名单的ip或黑名单的ip

  模拟URL参数检测:   浏览器输入http://192.168.3.140/?id=select * from name where name="goser",显示如下:

WAF上线注意事项

  初期上线只记录日志,不开启WAF,防止误杀

  WAF规则管理使用saltstack工具

  要知道并不是有了WAF就安全,安全在很大一部分是人为因素

posted @ 2018-08-30 14:35  goser  阅读(1886)  评论(0编辑  收藏  举报