Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××

Naxsi是一个开放源代码、高效、低维护规则的Nginx web应用防火墙(Web Application Firewall)模块。Naxsi的主要目标是加固web应用程序,以抵御SQL注入、跨站脚本、跨域伪造请求、本地和远程文件包含漏洞。
官网地址:https://github.com/nbs-system/naxsi
Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××

Naxsi 不要求任何特定的依赖,它需要的 libpcre ,libssl ,zlib ,gzip 这些 Nginx 已经集成了。

  • 下载Naxsi模块
[root@Super soft]# wget https://github.com/nbs-system/naxsi/archive/master.zip
[root@Super soft]# tar zxvf naxsi-master.zip
  • 重新编译Nginx添加Naxsi模块
[root@Super ~]# cd /opt/openresty/nginx/sbin/   #进入当前运行的目录
[root@Super sbin]# ./nginx -V                         #查看之前编译参数
nginx version: openresty/1.11.2.5
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/opt/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../iconv-nginx-module-0.14 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.10 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.18 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/opt/openresty/luajit/lib --with-http_realip_module --with-pcre --with-http_ssl_module                
[root@Super sbin]# cd /opt/soft/openresty-1.11.2.5/   #进入源码目录
[root@Super openresty-1.11.2.5]# ./configure --prefix=/opt/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../iconv-nginx-module-0.14 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.10 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.18 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/opt/openresty/luajit/lib --with-http_realip_module --with-pcre --with-http_ssl_module --add-module=/opt/soft/naxsi-master/naxsi_src/      #重新编译nginx,加入之前编译的参数,再加入naxsi模块,指定到naxsi的源码目录中的naxsi_src目录
......
./configure: error: no /opt/soft/openresty-1.11.2.5/../ngx_devel_kit-0.3.0/config was found       #报错,openresty的./nginx -V中模块路径../xxx的都是openresty默认添加的,这些路径是在根目录的build下,编译时候会自动加上
ERROR: failed to run command: sh ./configure --prefix=/opt/openresty/nginx/nginx \...
[root@Super openresty-1.11.2.5]#
[root@Super openresty-1.11.2.5]# ./configure --prefix=/opt/openresty/ --add-module=/opt/soft/naxsi-master/naxsi_src/ --with-luajit    #只需要加这几项
[root@Super openresty-1.11.2.5]# gmake
[root@Super openresty-1.11.2.5]# find . -type f -iname nginx
./build/nginx-1.11.2/objs/nginx
[root@Super openresty-1.11.2.5]# cp /opt/openresty/nginx/sbin/nginx{,.20180803bak}   #这步很重要
[root@Super openresty-1.11.2.5]# cp ./build/nginx-1.11.2/objs/nginx /opt/openresty/nginx/sbin/nginx
cp: overwrite ‘/opt/openresty/nginx/sbin/nginx’? y
cp: cannot create regular file ‘/opt/openresty/nginx/sbin/nginx’: Text file busy
[root@Super openresty-1.11.2.5]# killall -9 nginx
[root@Super openresty-1.11.2.5]# cp build/nginx-1.11.2/objs/nginx /opt/openresty/nginx/sbin/
cp: overwrite ‘/opt/openresty/nginx/sbin/nginx’? y
[root@Super openresty-1.11.2.5]# cd !$
cd /opt/openresty/nginx/sbin/
[root@Super sbin]# ./nginx -V
nginx version: openresty/1.11.2.5
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/opt/openresty//nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.10 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.18 --add-module=../redis2-nginx-module-0.14 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/opt/openresty/luajit/lib --add-module=/opt/soft/naxsi-master/naxsi_src --with-http_ssl_module
[root@Super sbin]#

复制Naxsi核心配置文件到nginx/conf下

[root@Super build]# cp /opt/soft/naxsi-master/naxsi_config/naxsi_core.rules /opt/openresty/nginx/conf/

在nginx.conf配置文件中添加Naxsi核心配置文件

[root@Super build]# cp /opt/openresty/nginx/conf/nginx.conf{,.20190804bak}
[root@Super build]# vim /opt/openresty/nginx/conf/nginx.conf
......
http {
    include       mime.types;
    include       naxsi_core.rules;      #加载naxsi 核心规则文件
        default_type  application/octet-stream;
        ......
        }
......      

配置Naxsi子规则,子规则是配置在Nginx配置文件的Server区块中的,子规则更多是起到配置过滤等级还有白名单的作用。新建文件naxsi.rules

[root@Super build]# vim /opt/openresty/nginx/conf/naxsi.rules
#LearningMode  启用学习模式,即拦截请求后不拒绝访问,只将触发规则的请求写入error_log选项定义的文件中。如果对规则产生的影响不太清楚,可以先设置为学习模式。启用学习模式不能起到拦截非法请求的防御的效果。建议先使用学习模式,规则测试完成后再启用拦截模式。这样可以避免出现对网站、服务器某些不可知的影响。启用学习模式不会拦截非法请求的防御的效果.
SecRulesEnabled;      #启用Naxsi模块,禁用模块:SecRulesDisabled
DeniedUrl "/RequestDenied";     # 拒绝访问时展示的页面
#check rules  设置各规则不同的触发阈值。 一旦该阈值触发,请求将被阻塞。
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
#naxsi log
error_log        logs/naxsi.log;    #nginx相对路径都是相对于根目录
[root@Super build]# 

虚拟主机添加支持Naxsi防×××

[root@Super build]# vim /opt/openresty/nginx/conf/nginx.conf
......
http {
        ......
                server {
                            ......
                                        location / {
                                                        ......
                                    include naxsi.rules;
                                                        ......
                                                       }           
                                        ......
                                        error_page   500 502 503 504  /50x.html;
                    location = /50x.html {
                                    root   html;
                     }
                    location /RequestDenied {       #定义naxsi.rules中DeniedUrl返回的代码
                                 return 403;
                    }
                    error_page  403  /403.html;
                    location = /403.html {
                                     root   html;
                    }
                                        ......
                   }
                ......
       }
......
root@Super build]# vim /opt/openresty/nginx/html/403.html
<html>
  <head>
    <title>Error 403 Request Denied</title>
  </head>
  <body>
    <h2>Error 403 Request Denied</h2>
    For some reasons,you request has been denied.
  </body>
<</html>
[root@Super build]# /opt/openresty/nginx/sbin/nginx -t -c /opt/openresty/nginx/conf/nginx.conf
nginx: the configuration file /opt/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /opt/openresty/nginx/conf/nginx.conf test is successful
[root@Super build]# cd /opt/openresty/nginx/sbin/
[root@Super sbin]# ./nginx -s reload

测试Naxsi
在一台测试机上执行Droplet http:// Your_Droplet_IP /index.html?asd=—- ;因为破折号用于SQL中的注释,因此被认为是SQL注入的一部分。

[root@localhost ~]# curl http:// Your_Droplet_IP /index.html?asd=----

nginx服务器日志

[root@Super sbin]# tail -500f /opt/openresty/nginx/logs/naxsi.log 
MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
2018/11/09 12:20:51 [error] 4088#0: *1 NAXSI_FMT: ip=X.X.X.X&server=Y.Y.Y.Y&uri=/index.html&learning=1&total_processed=24&total_blocked=1&zone0=ARGS&id0=1007&var_name0=asd, client: X.X.X.X, server: localhost, request: "GET /index.html?asd=---- HTTP/1.1", host: "Y.Y.Y.Y"

如果错误日志出现NAXSI_FMT的信息说明规则生效。

日志说明:
NAXSI_FMT由不同的项目组成:
ip :客户的IP
server:请求的主机名(如http标头中所示Host)
uri:请求的URI(没有参数,停在?)
learning:告诉naxsi是否处于学习模式(0/1)
vers:Naxsi版本,仅从0.51开始
total_processed:nginx的worker处理的请求总数
total_blocked:(naxsi)nginx的worker阻止的请求总数
zoneN:匹配发生的区域(参见下表中的“区域”)
idN:匹配的规则ID
var_nameN:拦截规则匹配时的参数名(可选)
cscoreN:命名分数标记
scoreN:关联的命名分数值,每一项naxsi规则都有对应的得分
几个zone,id,var_name,cscore和score组可以出现在一行中。
https://github.com/nbs-system/naxsi/wiki/naxsilogs

上面的日志:
zone0=ARGS&id0=1007&var_name0=asd 。 它提供区域(请求的部分),触发规则的ID和可疑请求的变量名称。
XXXX就是客户端的IP,而YYYY是Droplet的IP。 该URI还包含请求(的文件名index.htm ),事实上Naxsi仍然在学习模式(工作learning=1 ),并且所有处理的请求(总数total_processed=24 )。

naxsi_core.rules示例行:

MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;

MainRule是开始与每一个规则的指令。 类似地,每个规则以规则的id号结束。
str:在规则的第二部分中找到。 如果它是str:这意味着该签名将纯字符串,按照上述的例子。 正则表达式可以用指令来也符合rx:
msg:给出了一些澄清规则
mz:表示比赛区,或请求的哪一部分将被检查。 这可以是正文,URL,参数等
s:确定当发现签名将被分配的得分。 比分被添加到不同的柜台,如SQL (SQL***), RFI (远程文件包含***)等。
上述规则( id 1007 )与评论mysql comments意味着如果串–在一个请求(体,参数等)的任何部分被发现,4分将被添加到SQL计数器。触发规则1007,需要2双破折号(–),这是因为对于每一对,我们得到4点,SQL链需要8点来阻止请求。 因此,只有一对破折号不会有问题,在大多数情况下,合法流量不会受到影响。

再看一个特殊规则指令是negative 。如果签名不匹配,则应用分数,即您怀疑请求中的某些内容丢失时的怀疑活动。

  • 配置Naxsi白名单

基础规则(BasicRule)是用来创建白名单的记录。白名单规则语法:
BasicRule wl:ID [negative] [mz:[$URL:target_url]|[match_zone]|[$ARGS_VAR:varname]|[$BODY_VARS:varname]|[$HEADERS_VAR:varname]|[NAME]]

wl:ID (White List ID) 哪些拦截规则会进入白名单:
wl:0:把所有拦截规则加入白名单
wl:42:把ID为42的拦截规则加入白名单
wl:42,41,43:把ID为42, 41和43的拦截规则加入白名单
wl:-42:把所有拦截规则加入白名单,除了ID为42的拦截规则

mz:(Match Zones) 指定的区域会生效本条白名单:
ARGS: GET的整个参数,如: foo=bar&in=%20
$ARGS_VAR: GET参数的参数名, 如:foo=bar&in=%20中的foo和in
$ARGS_VAR_X: 正则匹配的GET参数的参数名
HEADERS: 整个HTTP协议头
$HEADERS_VAR: HTTP协议头的名字
$HEADERS_VAR_X: 正则匹配的HTTP协议头的名字
BODY: POST的整个参数内容
$BODY_VAR: POST参数的参数名
$BODY_VAR_X: 正则匹配的POST参数的参数名
URL: URL(?前的)
URL_X: 正则匹配的URL(?前的)
FILE_EXT: 文件名 (POST上传文件时上传的文件名)

Naxsi 社区提供了一些常用的白名单规则,例如 wordpress 。可以在 https://github.com/nbs-system/naxsi-rules 下载白名单规则。

白名单配置示例

以naxsi_core.rules中规则#1000为例:规则#1000是过滤包含select、update、delete、insert等SQL关键字的规则。

在上面Naxsi子规则配置文件naxsi.rules添加
BasicRule wl:1000; 因为没有指定区域,所以完全禁用拦截规则#1000
BasicRule wl:1000 “mz:$ARGS_VAR:foo”; 在全部GET参数名为foo的值中禁用拦截规则#1000,类似https://blog.51cto.com/?foo=select from demo这样的请示就不会被过滤。
BasicRule wl:1000 “mz:$ARGS_VAR:foo|$URL:/bar”; 在URL为/bar的GET请求中参数名为foo的值中禁用拦截规则#1000,类似https://blog.51cto.com/bar?foo=select
from demo不会被过滤。
BasicRule wl:1000 “mz:$URL:/bar|ARGS”; 在URL为/bar的GET请求中的参数禁用拦截规则#1000,类似https://blog.51cto.com/bar?my=select from demohttp://mike.hi-linux.com/bar?from=weibo均不会过滤
BasicRule wl:1000 “mz:ARGS|NAME”; 在全部GET请求中对所有参数名(只是名,不包含参数值)中禁用拦截规则#1000,类似https://blog.51cto.com/bar?from=weibo请求不会过滤;https://blog.51cto.com/bar?foo=select请求会过滤,因为select属于参数值,不在白名单范围内
BasicRule wl:0 “mz:$URL_X:^/upload/(.
).(.)$|URL”; 在全部请求中对符合^/upload/(.).(.*)$正则规则的URL禁用全部拦截规则,类似https://blog.51cto.com/upload/select.db请求不会被过滤(原本会触发#1000拦截规则)

添加白名单naxsi_BasicRule.conf

[root@Super sbin]# vim /opt/openresty/nginx/conf/naxsi_BasicRule.conf
BasicRule wl:0 "mz:$ARGS_VAR:script";
BasicRule wl:0 "mz:$ARGS_VAR:id";
[root@Super sbin]# ./nginx -s reload

表示xss×××正常是被拦截的,若被添加白名单,则不被拦截:此处是Get 参数名若为id 或者script,则不被拦截。

测试Naxsi

正常输入http://192.168.100.127 可以正常访问
Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××

在上面的地址后加上正常参数,例如:http://192.168.100.127/?id=1 或者 http://192.168.100.127/?id=1%20AND%201=1 返回的页面和上面一样,因为配置到白名单了

Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××

如果访问http://192.168.100.127/?name=40//and//1=1 会提示Error 403 Request Denied,含有条件注入被拦截了

Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××

访问http://192.168.100.127/?name=%28%29 会提示Error 403 Request Denied,含有特殊字符

Nginx使用Naxsi搭建Web应用防火墙(WAF),防xss、防注入×××