ngx_http_waf_module 是一个开源的、高效的、规则简单、策略易扩展的nginx WAF模块。
- Name
- Status
- Install
- Example Configuration
- Directives
- New match strategy
- Author
- Copyright and License
- See Also
ngx_http_waf_module 还处在早期的开发阶段。
./configure --prefix=/usr/local/nginx --add-dynamic-module=github.com/vislee/ngx_http_waf_module --with-compat
load_module ./modules/ngx_http_waf_module.so;
events {
......
}
http {
......
security_rule id:1001 "str:rx@^[a-z]{1,3}" "s:$BYPASS:1,$SQLI:2" z:V_HEADERS:bar|ARGS;
security_rule id:1002 "str:!sw@/test.php" s:$XSS:3,$BYPASS:3 z:#URL;
security_rule id:1003 "libinj:sql" "s:$SQLI:9" z:V_ARGS:foo;
security_rule id:1004 "libinj:decode_url|xss" "s:$XSS:9" z:V_ARGS:foo "note:test rule by vislee";
security_rule id:1005 "str:eq@testbody" "s:$BYPASS:2" "z:BODY";
security_rule id:1006 "str:decode_base64|decode_url|ct@test file data" "s:$BYPASS:2" "z:V_BODY:input";
security_rule id:1007 "str:ct@eval" "s:$HANG:2" "z:#FILE";
security_rule id:1008 "str:ct@testphp" "s:$HANG:2" "z:X_FILE:^[a-z]{1,5}\.php$";
security_rule id:1009 "str:ct@testphp" "s:$BYPASS:2" "z:#RAW_BODY";
map $sec_result $ups {
"block" block;
default runtime;
}
server {
location / {
client_body_buffer_size 1m;
security_waf on;
security_log ./logs/waf.log;
security_loc_rule wl:1003 z:V_ARGS:test;
security_loc_rule id:90001 str:eq@vislee s:$BYPASS:4 z:V_HEADERS:name;
security_check "$HANG>4" LOG;
security_check "$BYPASS>8" $sec_result;
security_check "$SQLI>8" DROP;
security_check "$XSS>8" BLOCK;
proxy_pass http://$ups;
}
}
语法 security_rule rule
默认: no
环境: http
配置一条通用的规则,对该http
下的所有location
均可见。
规则的格式: security_rule id:number strategy "s:$TAG:score,$TAG2:score" "z:zones" "note:message";
-
id: number取值为数字,规则的唯一编码,唯一代表一条规则。在
白名单
和日志
记录中使用。 -
strategy: 规则策略,有以下几种策略。 以
str:
开始的是字符串匹配策略。 以libinj:
开始的是调用了第三方 (libinjection)[https://github.com/client9/libinjection] 库的策略。 以hash:
开始的是计算hash值的策略。 以libmagic:
开始的是调用了libmagic库通过检测文件魔数获取文件类型。- "str:[decode_func1|decode_func2][!]le@string": [经过decode函数处理后的]字符串[不]小于等于string(字典顺序)
- "str:[decode_func1|decode_func2][!]ge@string": [经过decode函数处理后的]字符串[不]大于等于string(字典顺序)
- "str:[decode_func1|decode_func2][!]ct@string": [经过decode函数处理后的]字符串[不]包含string
- "str:[decode_func1|decode_func2][!]eq@string": [经过decode函数处理后的]字符串[不]等于string
- "str:[decode_func1|decode_func2][!]sw@string": [经过decode函数处理后的]字符串[不是]以string开始
- "str:[decode_func1|decode_func2][!]ew@string": [经过decode函数处理后的]字符串[不是]以string结束
- "str:[decode_func1|decode_func2][!]rx@regex": [经过decode函数处理后的]字符串[不]符合正则表达式
- "libinj:[decode_func1|decode_func2][!]sql": [经过decode函数处理后的]字符串[不]存在sql注入
- "libinj:[decode_func1|decode_func2][!]xss": [经过decode函数处理后的]字符串[不]存在xss攻击
- "hash:[!]md5@hashcode": 字符串的md5值[不]等于hashcode
- "hash:[!]crc32@hashcode": 字符串的crc32[不]等于hashcode
- "hash:[!]crc32_long@hashcode": 字符串的crc32_long[不]等于hashcode
- "libmagic:[!]mime_type@type": 内容的魔数识别[不]等于type
decode_func: 支持
decode_url
和decode_base64
函数。通过管道符号(|
)支持多次decode操作。
!: 取反。
-
"s:$TAG:score,$TAG2:score": 规则标签打分,通过该配置多条规则可以加权作用。如果不配置,命中该规则请求被BLOCK。
-
zones: 规则策略检测的区域,有以下多种取值。其中
@
代表仅检测对应的key,#
代表仅检测对应的value。- "z:#URL": 检测请求的URL。
- "z:V_URL:string": 检测是string的URL。
- "z:X_URL:regex": 检测满足regex正则表达式的URL。
- "z:[@ | #]ARGS": 检测请求的URL参数,@ARGS表示仅检测参数的key,#ARGS表示仅检测参数的value。
- "z:V_ARGS:string": 检测请求参数是string参的value。
- "z:X_ARGS:regex": 检测请求参数满足regex正则表达式的value。
- "z:[@ | #]HEADERS": 检测请求的头。
- "z:V_HEADERS:string": 检测请求头是string的value。
- "z:X_HEADERS:regex": 检测请求头符合regex正则表达式的value。
- "z:#RAW_BODY": 检测请求的原始(未解码)body。
- "z:[@ | #]BODY": 检测请求解析后的body,支持urldecode和multipart的解析。
- "z:V_BODY:string": 检测解析后的body的string的value。
- "z:X_BODY:regex": 检测body解析后的复合正则表达式的value。
- "z:#FILE": 检测表单上传的文件内容。
- "z:X_FILE:regex": 检测表单上传文件名满足正则表达式的内容。
例如:
security_rule id:100 "str:decode_base64|decode_url|!eq@foo bar" "s:$ATT:2,$SQLI:1" "z:V_ARGS:foo|#HEADERS" "note:test rule";
一个请求的
URL参数foo的value如果先base64解码然后再URL解码的结果不等于"foo bar",则会对$ATT
这个变量累加2分,对$SQLI
这个变量累加1分。
任一请求头的value如果先base64解码然后再URL解码的结果不等于"foo bar",则会对$ATT
这个变量累加2分,对$SQLI
这个变量累加1分。
语法 security_loc_rule rule
默认: no
环境: location
配置仅对当前location
可见的规则。
也可以配置屏蔽全局规则的白名单。
白名单的格式:security_loc_rule "wl:id1,id2" "z:zones" "note:test whitelist";
语法 security_waf <on|off>
默认: off
环境: location
在对应的location
开启规则检测。
语法 security_check $tag>threshold <LOG|BLOCK|DROP|ALLOW|$variable>
默认: no
环境: location
设置规则打分的阈值和对应的动作。
支持的动作有: LOG: 仅记录一条日志。 BLOCK: 拒绝请求,返回403. DROP: 关闭连接。 ALLOW: 跳过剩余规则。 $variable: 满足条件该变量的值为"block",和map指令配合使用。
例如:
security_check "$ATT>5" BLOCK;
当ATT这个变量的分数累加超过5时,请求被BLOCK。
语法 security_log <logfile|off> [unflat]
默认: off
环境: location
以json格式记录命中的规则和请求。
支持以syslog:
开始的配置,把日志通过syslog的形式记录。
语法: security_timeout time
默认: 60s
环境: location
单个请求规则过滤时长。
如果现有策略不满足需求,新的策略扩展也是非常容易的。仅需要开发两个函数,注册到模块中就可以了。例如调用了libinjection这个库扩展sql注入检测。
// The parse directive function
static ngx_int_t
ngx_http_waf_parse_rule_libinj(ngx_conf_t *cf, ngx_str_t *str,
ngx_http_waf_rule_parser_t *parser, ngx_http_waf_rule_opt_t *opt)
{
u_char *p, *e;
ngx_int_t offset;
static ngx_str_t sql = ngx_string("sql");
static ngx_str_t xss = ngx_string("xss");
if (str->len - parser->prefix.len < 3) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid libinj in arguments \"%V\"", str);
return NGX_ERROR;
}
p = str->data + parser->prefix.len;
e = str->data + str->len;
offset = ngx_http_waf_parse_rule_decode(cf, opt->p_rule->decode_handlers,
p, e - 4);
if (offset == NGX_ERROR) {
return NGX_ERROR;
}
p += offset;
if ((size_t)(e - p) == sql.len
&& ngx_strncmp(p, sql.data, sql.len) == 0)
{
opt->p_rule->str = sql;
opt->p_rule->handler = ngx_http_waf_rule_str_sqli_handler;
} else if ((size_t)(e - p) == xss.len
&& ngx_strncmp(p, xss.data, xss.len) == 0)
{
opt->p_rule->str = xss;
opt->p_rule->handler = ngx_http_waf_rule_str_xss_handler;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid libinj args in arguments \"%V\"", str);
return NGX_ERROR;
}
return NGX_OK;
}
// matching-strategy callback function
static ngx_int_t
ngx_http_waf_rule_str_sqli_handler(ngx_http_waf_public_rule_t *pr,
ngx_str_t *s)
{
ngx_int_t issqli;
struct libinjection_sqli_state state;
if (s == NULL || s->data == NULL || s->len == 0) {
return NGX_ERROR;
}
libinjection_sqli_init(&state, (const char *)s->data, s->len, FLAG_NONE);
issqli = libinjection_is_sqli(&state);
if (!issqli) {
return NGX_ERROR;
}
return NGX_OK;
}
// registered parse directive function
static ngx_http_waf_rule_parser_t ngx_http_waf_rule_parser_item[] = {
......
{ngx_string("libinj:"), ngx_http_waf_parse_rule_libinj},
......
}
wenqiang li(vislee)
This module is licensed under the GPL license.
Copyright (C) 2018-2019, by vislee.
All rights reserved.
- nginx: ngx_http_waf_module 是nginx的一个模块.
- naxsi: ngx_http_waf_module 从naxsi吸取了很多灵感.
- libinjection: ngx_http_waf_module sqli和xss调用了该库.
- Hyperscan: 若安装了该库,则会用该库替换掉libPCRE这个默认的正则库.
- libmagic: ngx_http_waf_module 引用该库用来识别文件类型.