You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
uuWAF/docs/api/README.md

660 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## :strawberry: 全局配置
### init_by_lua_block
##### conf
- 类型: ``table``
- 默认值: ``初始化南墙配置``
- 用法:
```lua
waf = require("waf")
local conf = {
id = "1", -- 当使用集群时,用于区分不同的南墙主机
db = { host = "127.0.0.1", port = 3306, user = "root", password = "Safe3.WAF" }, -- 数据库连接配置
ml = { server = "http://127.0.0.1:4445", access_token = "secret" } -- 机器学习服务连接配置
}
waf.http_init(conf)
```
local conf变量位于/uuwaf/conf/uuwaf.conf中用于初始化南墙配置。
## :grapes: 规则
?>这里对规则所用到的一些变量和相关函数进行说明更多规则编写方法请大家参照WAF管理后台中的规则管理当中的众多实际例子。规则模板见https://github.com/Safe3/uuWAF/blob/main/rules/anti-cc.lua 一条防cc攻击的安全规则。欢迎各位贡献安全规则详情见https://waf.uusec.com/#/guide/contribute 。
### 规则示例
```lua
--[[
规则名称: anti cc
过滤阶段: 请求阶段
危险等级: 中危
规则描述: 当一分钟访问/api/路径频率超过360次则在5分钟内拦截该ip访问
--]]
if not waf.startWith(waf.toLower(waf.uri), "/api/") then
return false
end
local sh = waf.ipCache
local ccIp = 'cc-' .. waf.ip
local c, f = sh:get(ccIp)
if not c then
sh:set(ccIp, 1, 60, 1) -- 设置1分钟也就是60秒访问计数时间
else
if f == 2 then
return waf.block(true) -- 重置TCP连接不记录日志
end
sh:incr(ccIp, 1)
if c + 1 >= 360 then -- 频率超过360次
sh:set(ccIp, c + 1, 300, 2) -- 设置5分钟也就是300秒拦截时间
return true, ccIp, true -- 返回参数第一个true为是否检测到第二个参数ccIp为日志记录内容第三个参数true表示拦截false表示只记录不拦截
end
end
return false
```
### 规则变量
#### 请求阶段变量
##### waf.ip
- 类型: ``string``
- 默认值: ``客户端访问ip``
- 用法: 只读用于获取客户端访问ip可以在WAF后台站点管理中配置客户端ip来源获取方式为Socket或X-Forwarded-For中的倒序第n个ip。
##### waf.scheme
- 类型: ``string``
- 默认值: ``客户端访问http协议值为字符串http或https``
- 用法: 只读。
##### waf.httpVersion
- 类型: ``number``
- 默认值: ``http协议版本值为1.0、1.1、2.0、3.0``
- 用法: 只读。
##### waf.host
- 类型: ``string``
- 默认值: ``客户端访问host头``
- 用法: 只读。
##### waf.ipBlock
- 类型: ``table``
- 默认值: ``键值存储库用于存放已拦截的客户端ip``
- 用法: 见ngx.shared.DICT。
##### waf.ipCache
- 类型: ``table``
- 默认值: ``键值存储库用于存放访问的客户端ip``
- 用法: 见ngx.shared.DICT。
##### waf.requestLine
- 类型: ``string``
- 默认值: ``原始的request line数据``
- 用法: 只读。
##### waf.uri
- 类型: ``string``
- 默认值: ``解码处理过的URI不带参数``
- 用法: 只读。
##### waf.method
- 类型: ``string``
- 默认值: ``请求方法``
- 用法: 只读。
##### waf.reqUri
- 类型: ``string``
- 默认值: ``原始URI带参数``
- 用法: 只读。
##### waf.userAgent
- 类型: ``string``
- 默认值: ``客户端请求的User-Agent头数据``
- 用法: 只读。
##### waf.referer
- 类型: ``string``
- 默认值: ``客户端请求的Referer头数据``
- 用法: 只读。
##### waf.reqContentType
- 类型: ``string``
- 默认值: ``客户端请求的Content-Type头数据``
- 用法: 只读。
##### waf.XFF
- 类型: ``string``
- 默认值: ``客户端请求的X-Forwarded-For头数据``
- 用法: 只读。
##### waf.origin
- 类型: ``string``
- 默认值: ``客户端请求的Origin头数据``
- 用法: 只读。
##### waf.reqHeaders
- 类型: ``table``
- 默认值: ``请求的所有header对象``
- 用法: 只读。
##### waf.hErr
- 类型: ``string``
- 默认值: ``请求header解析出错信息``
- 用法: 只读。
##### waf.isQueryString
- 类型: ``bool``
- 默认值: ``true或false``
- 用法: 只读,是否存在请求参数。
##### waf.reqContentLength
- 类型: ``number``
- 默认值: ``0``
- 用法: 只读请求body内容长度整数值。
##### waf.queryString
- 类型: ``table``
- 默认值: ``请求url参数key、value``
- 用法: 只读。
##### waf.qErr
- 类型: ``string``
- 默认值: ``请求参数解析出错信息``
- 用法: 只读。
##### waf.form
- 类型: ``table``
- 默认值: ``请求body对象``
- 用法: 只读。
##### waf.form["RAW"]
- 类型: ``string``
- 默认值: ``请求body的原始数据``
- 用法: 只读。
##### waf.form["FORM"]
- 类型: ``table``
- 默认值: ``请求body参数key、value``
- 用法: 只读,表单如: {uid="12",vid={[1]="select",[2]="a from b"}}。
##### waf.form["FILES"]
- 类型: ``table``
- 默认值: ``解析出的请求body中上传文件信息``
- 用法: 只读,文件信息如: {name={[1]="filename",[2]="file content"}}。
##### waf.fErr
- 类型: ``string``
- 默认值: ``解析请求body出错信息``
- 用法: 只读,一般是恶意畸形请求包。
##### waf.cookies
- 类型: ``table``
- 默认值: ``请求cookie参数key、value``
- 用法: 只读。
##### waf.cErr
- 类型: ``string``
- 默认值: ``解析请求cookie出错信息``
- 用法: 只读。
#### 返回http头阶段新增变量
##### waf.status
- 类型: ``number``
- 默认值: ``返回http状态整数值``
- 用法: 只读。
##### waf.respHeaders
- 类型: ``table``
- 默认值: ``返回的所有header对象key、value``
- 用法: 只读。
##### waf.respContentLength
- 类型: ``number``
- 默认值: ``返回body内容长度整数值``
- 用法: 只读。
##### waf.respContentType
- 类型: ``string``
- 默认值: ``服务端返回的Content-Type头数据``
- 用法: 只读。
#### 返回页面阶段新增变量
##### waf.respBody
- 类型: ``string``
- 默认值: ``返回body页面内容``
- 用法: 只读。
##### waf.replaceFilter
- 类型: ``bool``
- 默认值: ``false``
- 用法: 当返回内容类型为text/html、text/plain、json、xml时通知南墙替换返回页面内容则设置waf.replaceFilter = true可用于数据脱敏、敏感词替换等场景。
##### 规则示例
```lua
--[[
规则名称: datamask
过滤阶段: 返回内容阶段
危险等级: 中危
规则描述: 对返回页面中的身份证和手机号进行*替换脱敏
--]]
if waf.respContentLength == 0 or waf.respContentLength >= 2097152 then
return
end
-- 只保留身份证号前2位和后2位
local newstr, _, err = waf.rgxGsub(waf.respBody, [[\b((1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5]|[7-9]1)\d{4}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx])\b]], function(m)
return m[0]:sub(1, 2) .. "**************" .. m[0]:sub(-2)
end, "jos")
if not newstr then
waf.errLog("error: ", err)
return
end
waf.respBody = newstr
-- 只保留手机号前3位和后4位
newstr, _, err = waf.rgxGsub(waf.respBody, [[\b1(?:(((3[0-9])|(4[5-9])|(5[0-35-9])|(6[2,5-7])|(7[0135-8])|(8[0-9])|(9[0-35-9]))[ -]?\d{4}[ -]?\d{4})|((74)[ -]?[0-5]\d{3}[ -]?\d{4}))\b]], function(m)
return m[0]:sub(1, 3) .. "****" .. m[0]:sub(-4)
end, "jos")
if not newstr then
waf.errLog("error: ", err)
return
end
waf.respBody = newstr
-- 通知南墙进行数据替换
waf.replaceFilter = true
```
### 规则 API
#### 规则通用 API
##### waf.startWith(sstr,dstr)
- 参数: ``sstr 为原字符串dstr 为查找字符串``
- 功能: 判断字符串 sstr 是否以 dstr 开头
- 返回值: ``true 或 false``
##### waf.endWith(sstr,dstr)
- 参数: ``sstr 为原字符串dstr 为查找字符串``
- 功能: 判断字符串 sstr 是否以 dstr 结尾
- 返回值: ``true 或 false``
##### waf.toLower(sstr)
- 参数: ``sstr 为原字符串``
- 功能: 将字符串 sstr 转化为小写
- 返回值: ``sstr 小写``
##### waf.contains(sstr,dstr)
- 参数: ``sstr 为原字符串dstr 为查找字符串``
- 功能: 判断字符串 sstr 是否在字符串 dstr
- 返回值: ``true 或 false``
##### waf.regex(sstr,pat,ext)
- 参数: ``sstr 为原字符串pat 为正则表达式ext 为正则属性``
- 功能: 在字符串 sstr 中匹配正则表达式 pat用法同ngx.re.match
- 返回值: ``所有匹配项、错误``
##### waf.rgxMatch(sstr,pat,ext)
- 参数: ``sstr 为原字符串pat 为正则表达式ext 为正则属性``
- 功能: 在字符串 sstr 中匹配正则表达式 pat
- 返回值: ``true 或 false``
##### waf.rgxGmatch(sstr,pat,ext)
- 参数: ``sstr 为原字符串pat 为正则表达式ext 为正则属性``
- 功能: 在字符串 sstr 中匹配正则表达式 pat用法同ngx.re.gmatch
- 返回值: ``迭代器iterator,错误err``
##### waf.rgxSub(subject, regex, replace, options?)
- 参数: ``subject 为原字符串regex 为正则表达式replace 为要替换的字符串options为正则选项``
- 功能: 替换字符串 subject 中正则表达式 regex 匹配到的内容为 replace用法同ngx.re.sub
- 返回值: ``newstr, n, err分别为新字符串、替换个数、错误信息``
##### waf.rgxGsub(subject, regex, replace, options?)
- 参数: ``subject 为原字符串regex 为正则表达式replace 为要替换的字符串options为正则选项``
- 功能: 替换字符串 subject 中所有正则表达式 regex 匹配到的内容为 replace用法同ngx.re.gsub
- 返回值: ``newstr, n, err分别为新字符串、替换个数、错误信息``
##### waf.kvFilter(v,match,valOnly)
- 参数: ``v 为要匹配对象match 为匹配函数,valOnly 为 true 则只匹配 value``
- 功能: 用于匹配 cookie、queryString 等 keyvalue 键值对数据,在对象 v 中用 match 函 数匹配内容
- 返回值: ``true,匹配内容或 false,nil``
##### waf.knFilter(v,match,p)
- 参数: ``v 为要匹配对象match 为匹配函数p 为 1 时匹配上传文件名,为 0 时文件内容``
- 功能: 用于过滤上传文件信息,在对象 v 中用 match 函数匹配内容
- 返回值: ``true,匹配内容或 false,nil``
##### waf.jsonFilter(v, match,parsed,valOnly)
- 参数: ``v 为要匹配对象match 为匹配函数parsed 为 false 时解析类型为字符串 v 值,为 true 时解析类型为 table 的 v 值, valOnly 为 true 则只匹配 value``
- 功能: 用于遍历过滤请求中的 json 数据,在对象 v 中用 match 函数匹配内容
- 返回值: ``true,匹配内容或 false,nil``
##### waf.base64Decode(str)
- 参数: ``str 为要解码的 base64 字符串``
- 功能: 用于解码 base64 数据为明文数据
- 返回值: ``明文字符串或 nil``
##### waf.checkSQLI(str, level?)
- 参数: ``str 为要检测的字符串level可省略为严格等级数值越大越严格范围0至3``
- 功能: 基于语义引擎检测 sql 注入攻击
- 返回值: ``true 或 false``
##### waf.checkRCE(str, level?)
- 参数: ``str 为要检测的字符串level可省略为严格等级数值越大越严格范围0至3``
- 功能: 基于语义引擎检测命令注入攻击
- 返回值: ``true 或 false``
##### waf.checkPT(str)
- 参数: ``str 为要检测的字符串``
- 功能: 基于语义引擎检测路径遍历攻击
- 返回值: ``true 或 false``
##### waf.checkXSS(str)
- 参数: ``str 为要检测的字符串``
- 功能: 基于语义引擎检测xss攻击
- 返回值: ``true 或 false``
##### waf.strCounter(sstr,dstr)
- 参数: ``sstr 为原字符串dstr 为查找字符串``
- 功能: 计算字符串 dstr 在 sstr 中出现的次数
- 返回值: ``整数``
##### waf.trim(str)
- 参数: ``str 为原字符串``
- 功能: 去掉字符串 str 两边的空格
- 返回值: ``去掉两边空格后的字符串``
##### waf.inArray(str,arr)
- 参数: ``str 为原字符串arr为字符串数组``
- 功能: 判断字符串 str 是否存在于arr字符串数组中
- 返回值: ``true 或 false``
##### waf.pmMatch(sstr,dict)
- 参数: ``sstr 为原字符串dict 为查找字典,以 lua 表的形式,如:{“aaa”, “bbb”, “ccc”}``
- 功能: 高效多模匹配多个字符串,发现其中一个字符串立即返回
- 返回值: ``true字典中的字符串或 falsenil``
##### waf.urlDecode(sstr)
- 参数: ``sstr 为原字符串``
- 功能: 将 sstr 进行 url 解码还原成字符串
- 返回值: ``解码后的字符串``
##### waf.htmlEntityDecode(sstr)
- 参数: ``sstr 为原字符串``
- 功能: 将字符串 sstr 进行 html 实体解码
- 返回值: ``解码后的字符串``
##### waf.hexDecode(sstr)
- 参数: ``sstr 为原字符串``
- 功能: 将字符串 sstr 进行 hex 解码
- 返回值: ``解码后的字符串``
##### waf.block(reset)
- 参数: ``reset 为true时直接重置tcp不返回任何内容否则返回403页面``
- 功能: 拦截客户端请求直接重置客户端连接或返回403页面与return搭配使用
##### waf.checkRobot(waf)
- 参数: ``waf 为固定waf对象无需修改``
- 功能: 检测机器人攻击如数据爬虫、扫描攻击、CC拒绝服务攻击等并生成滑动旋转图片验证码与return搭配使用
##### waf.redirect(uri, status?)
- 参数: ``uri为重定向的链接status为返回http状态可选默认为302``
- 功能: 重定向客户端请求到新的链接与return搭配使用
##### waf.ip2loc(ip)
- 参数: ``ip地址``
- 功能: 将ip地址转化为国家、省份、城市中文地理位置信息
- 返回值: ``country、 province、 city中国、湖北省、武汉市``
##### waf.errLog(...)
- 参数: ``1个或多个字符串``
- 功能: 记录错误日志到/uuwaf/logs/error.log中
- 返回值: ``无``
## :melon: 插件
?>南墙支持强大的插件扩展功能方便用户自行实现一些特有功能。插件模板见https://github.com/Safe3/uuWAF/blob/main/plugins/kafka-logger.lua 一个kafka日志记录插件。欢迎各位贡献安全插件详情见https://waf.uusec.com/#/guide/contribute 。
### 插件编写
一个标准的插件包含以下几个部分每个部分若无功能实现可省略每个大阶段分为pre和post前后两个小阶段分别代表南墙逻辑处理执行前和南墙逻辑处理执行后。南墙v4.1.0之前的版本没有小阶段请使用req_filter、resp_header_filter、resp_body_filter、log。
```lua
local _M = {
version = 0.1, -- 插件版本
name = "kafka-logger" -- 插件名称
}
-- ssl阶段前过滤
function _M.ssl_pre_filter(waf)
end
-- ssl阶段后过滤
function _M.ssl_post_filter(waf)
end
-- 请求阶段前过滤
function _M.req_pre_filter(waf)
end
-- 请求阶段后过滤
function _M.req_post_filter(waf)
end
-- 返回header阶段前过滤
function _M.resp_header_pre_filter(waf)
end
-- 返回header阶段后过滤
function _M.resp_header_post_filter(waf)
end
-- 返回body阶段前过滤
function _M.resp_body_pre_filter(waf)
end
-- 返回body阶段后过滤
function _M.resp_body_post_filter(waf)
end
-- 日志记录阶段前过滤
function _M.log_pre_filter(waf)
end
-- 日志记录阶段后过滤
function _M.log_post_filter(waf)
end
return _M
```
- #### SSL阶段过滤函数
- 该阶段用于获取客户端请求的域名和设置SSL证书waf变量的值为nil。
- #### 请求阶段过滤函数
- 该阶段用于过滤客户端发送的请求数据waf变量同规则变量一致可自行实现该函数功能。
- #### 返回header阶段过滤函数
- 该阶段用于过滤服务器返回的header头数据waf变量同规则变量一致可自行实现该函数功能。
- #### 返回body阶段过滤函数
- 该阶段用于过滤服务器返回的body内容数据waf变量同规则变量一致可自行实现该函数功能。
- #### 日志记录阶段执行函数
- 该阶段用于日志记录阶段做一些日志处理与推送waf变量同规则变量一致可自行实现该函数功能。
### 插件使用
1. 将插件文件如kafka-logger.lua 放于/uuwaf/waf/plugins/目录并修改文件扩展名为kafka-logger.w。
2. 修改/uuwaf/conf/uuwaf.conf文件在init_by_lua_block段中waf = require("waf")下新增一行waf:use("插件名称")如启用kafka-logger.w插件的示例如下
```lua
waf = require("waf")
waf:use("kafka-logger")
```
3. 执行/uuwaf/waf-service -s restart使插件生效如果插件代码运行有问题可以在/uuwaf/logs/error.log中查看详细错误信息。
### 常用功能函数
#### 各阶段数据共享
##### waf.ctx
有时为了在各个执行函数间共享同一个数据可以通过给waf.ctx赋值来实现
```lua
function _M.resp_body_pre_filter(waf)
waf.ctx = "share"
end
function _M.log_pre_filter(waf)
log.errLog(waf.ctx)
end
```
#### 记录错误日志
```lua
local log = require("waf.log")
```
##### log.errLog(...)
- 参数: ``可变参数,类型为字符串``
- 功能: 将信息写入错误日志/uuwaf/logs/error.log
- 返回值: ``无``
##### log.utf8(str)
- 参数: ``字符串``
- 功能: 将str字符编码转换为utf-8编码防止数据写入数据库或json编码时出错
- 返回值: ``字符串``
##### log.getReq()
- 参数: ``无``
- 功能: 获取客户端http请求信息
- 返回值: ``字符串``
##### log.encodeJson(obj)
- 参数: ``lua table对象``
- 功能: 将lua table对象转化json字符串
- 返回值: ``json字符串``
##### log.broker(func...)
- 参数: ``func为函数可变参数为传给函数func的参数``
- 功能: 代理执行函数func并传参。
- 返回值: ``无``
:smile: 其它隐藏功能彩蛋,由用户自行去发掘。