jinhailang / blog

技术博客:知其然,知其所以然
https://github.com/jinhailang/blog/issues
60 stars 6 forks source link

lua-resty-waf 实践总结 #30

Open jinhailang opened 6 years ago

jinhailang commented 6 years ago

lua-resty-waf 实践总结

lua-resty-waf 是基于 OpenResty 开发的 WAF 项目,其核心的防护规则策略基本与 ModSecurity Core Rule 一致,但是具体实现有所不同。

下面主要分四块阐述其实现功能与原理:

配置模块

系统配置分为两块系统基本配置和规则配置。

规则配置

系统默认提供了基础的防护规则集,规则集文件都在 rules/ 文件夹下,默认有九个文件:

默认规则集的执行顺序也是从上到下的,注意文件的命名规律,后续添加自己的规则集的时候最好遵循这种规范。

规则配置的加载方式有三种:

1)系统启动时默认加载

waf.init 函数默认会去 package.path 前缀路径下的目录 rules/ 下加载数组 global_rulesets 指定的 .json 规则集配置文件。 global_rulesets 默认就是包括了 rules/ 目录下的所有文件名,也是基本的系统参数之一,可以通过 waf:set_option("global_rulesets", {}) 来指定,具体后面会说。

2)使用 load_secrules 加载

可以调用函数 load_secrules 函数从磁盘加载 ModSecurity SecRules 配置文件,参数就是文件所在的绝对路径。需要注意的是还需要调用函数 add_ruleset 将规则集名(文件名称)注册到系统,否则系统是识别不到的。

系统内部会按行将 ModSecurity 规则集文件转换(在 translate 包内)成 waf 对应的规则格式(json)。目前支持四种规则指令:

3)使用 add_ruleset_string 加载

add_ruleset_string 可以直接加载规则集字符串(json)。 用户可以使用这种方式动态的加载自定义规则集合。

在每个阶段执行对应的规则集之前,都会先合并(merge)规则集,主要就是根据规则集名,当规则集名称一样时,用户自己添加的规则集优先级更高。

系统配置

waf.new 函数会初始化一个系统基础参数表,这些参数都可以通过函数 waf.set_option(参数名称, value) 来设置。 一些比较重要的参数说明:

规则模块

规则模块是 WAF 项目的核心,包括解析和执行两个部分,为了支持类似 ModSecurity 的防护规则,规则配置比较复杂,解析和执行逻辑就更复杂了。

规则解析

规则配置是按规则集(规则数组)的形式被读取的,规则集再分为多个阶段 --- access,header_filter 等。所有的规则集在解析时,会按阶段的维度,添加到对应阶段的规则集数组。

需要注意的是,规则解析时会计算两个特殊的变量值:

这两个变量值一般都是 1,即直接进入相邻的下个规则,但是使用 skipCHAIN 都会改变这些值。他们都被放入 table 对象 rule,供规则执行时使用。

规则配置项

规则执行

规则是在 waf.exec 函数内执行的,每个阶段只会执行当前阶段对应的规则集,及规则集里面的规则。需要注意的是 CHAIN 规则的执行逻辑,这种类型的规则会组成一个规则链,规则链内的规则是 and 关系,即规则匹配失败,就会跳过当前整个规则链。规则链是怎么组成的呢?当遇到非 CHAIN 规则时,就会计算成一个规则链。

下面使用 C 代表 CHAIN 规则,X 代表非 CHAIN 规则,有如下规则集:

C C C X X C X

将生成两条规则链:

日志模块

规则命中后,都会将命中(匹配成功)规则日志记录到日志输出缓存数组,除了 CHAIN 类型的规则,也就是说规则链命中后只会记录一条日志。 规则日志可以在阶段结束时输出,也可以在请求结束时,汇总一起输出。

日志是以 json 格式输出的,包括以下字段:

日志对外输出方式由 _event_log_target 设置,有三种输出方式:

特别说明

虽然,系统支持通过函数 load_secrules 直接加载 ModSecurity 规则集文件,但是,最好别这么干,因为自动转换过程交繁琐,不小心就容易出错。

V1og commented 5 years ago

赞👍

Veitor commented 3 years ago

总结很到位。。这rule规则看得我头都晕了