lua-resty-waf

基于 OpenResty 栈构建的高性能 WAF

$ opm get p0pr0ck5/lua-resty-waf

##名称

lua-resty-waf - 基于 OpenResty 栈构建的高性能 WAF

##目录

##状态

[!构建状态](https://travis-ci.org/p0pr0ck5/lua-resty-waf) [!Codewake](https://www.codewake.com/p/lua-resty-waf)

lua-resty-waf 目前处于积极开发中。在问题跟踪器中打开的新错误和问题将在一天或两天内得到答复,性能影响和安全相关问题将优先得到修复。当开发资源可用时,将添加更大的功能集和增强功能(有关计划功能的概述,请参见路线图部分)。

lua-resty-waf 与 lua-resty-core 的 master 分支兼容。最近版本的 OpenResty(>= 1.9.7.4)中提供的 lua-resty-core 的捆绑版本与 lua-resty-waf 兼容;与旧版 OpenResty 捆绑包捆绑在一起的版本则不兼容,因此希望利用 resty.core 的用户需要将本地版本替换为 GitHub 项目 中提供的版本,或根据此提交修补模块。

##描述

lua-resty-waf 是使用 OpenResty 栈构建的反向代理 WAF。它使用 Nginx Lua API 来分析 HTTP 请求信息并针对灵活的规则结构进行处理。lua-resty-waf 附带一个模仿 ModSecurity CRS 的规则集,以及在初始开发和测试过程中构建的一些自定义规则,以及一个用于新兴威胁的小型虚拟补丁集。此外,lua-resty-waf 还附带用于自动翻译现有 ModSecurity 规则的工具,允许用户扩展 lua-resty-waf 的实现,而无需学习新的规则语法。

lua-resty-waf 最初由 Robert Paprocki 为其在西方州长大学的硕士论文开发。

##要求

lua-resty-waf 需要几个第三方 resty lua 模块,但这些模块都与 lua-resty-waf 打包在一起,因此无需单独安装。建议在运行 OpenResty 软件包的系统上安装 lua-resty-waf;lua-resty-waf 尚未在使用单独的 Nginx 源代码和 Nginx Lua 模块包构建的平台上进行测试。

为了获得最佳的正则表达式编译性能,建议使用支持 JIT 编译的 PCRE 版本构建 Nginx/OpenResty。如果您的操作系统没有提供此功能,您可以将支持 JIT 的 PCRE 直接构建到 Nginx/OpenResty 构建中。为此,请在 --with-pcre 配置标志中引用 PCRE 源代码的路径。例如

        # ./configure --with-pcre=/path/to/pcre/source --with-pcre-jit

您可以从 PCRE 网站 下载 PCRE 源代码。另请参见 这篇博文,了解使用支持 JIT 的 PCRE 库构建 OpenResty 的分步指南。

##性能

lua-resty-waf 的设计理念是效率和可扩展性。它利用 Nginx 的异步处理模型和高效的设计,尽可能快地处理每个事务。负载测试表明,实现所有提供的规则集(旨在模仿 ModSecurity CRS 背后的逻辑)的部署,每秒处理大约 300-500 微秒的请求;这等于 Cloudflare 的 WAF 所宣传的性能。测试是在合理的硬件栈(E3-1230 CPU、32 GB RAM、2 x 840 EVO RAID 0)上运行的,峰值约为每秒 15,000 个请求。有关更多信息,请参见 这篇博文

lua-resty-waf 的工作负载几乎完全是 CPU 绑定的。Lua VM 中的内存占用(不包括由 lua-shared-dict 支持的持久存储)约为 2MB。

##安装

提供了一个简单的 Makefile

        # make && sudo make install

或者,通过 Luarocks 安装

        # luarocks install lua-resty-waf

请注意,默认情况下,lua-resty-waf 以模拟模式运行,以防止立即影响应用程序;希望启用规则操作的用户必须显式将操作模式设置为 ACTIVE。

##概要

        http {
                -- include lua_resty_waf in the appropriate paths
                lua_package_path '/usr/local/openresty/lualib/lua_resty_waf/?.lua;;';
                lua_package_cpath '/usr/local/openresty/lualib/lua_resty_waf/?.lua;;';
    
                init_by_lua '
                        -- use resty.core for performance improvement, see the status note above
                        require "resty.core"
    
                        -- require the base module
                        local lua_resty_waf = require "waf"
    
                        -- define options that will be inherited across all scopes
                        lua_resty_waf.default_option("debug", true)
                        lua_resty_waf.default_option("mode", "ACTIVE")
    
                        -- this may be desirable for low-traffic or testing sites
                        -- by default, event logs are not written until the buffer is full
                        -- for testing, flush the log buffer every 5 seconds
                        lua_resty_waf.default_option("event_log_periodic_flush", 5)
    
                        -- perform some preloading and optimization
                        lua_resty_waf.init()
                ';
        }
    
        server {
                location / {
                        access_by_lua '
                                local lua_resty_waf = require "waf"
    
                                local waf = lua_resty_waf:new()
    
                                -- default options can be overridden
                                waf:set_option("debug", false)
    
                                -- run the firewall
                                waf:exec()
                        ';
    
                        header_filter_by_lua '
                                local lua_resty_waf = require "waf"
    
                                -- note that options set in previous handlers (in the same scope)
                                -- do not need to be set again
                                local waf = lua_resty_waf:new()
    
                                waf:exec()
                        ';
    
                        body_filter_by_lua '
                                local lua_resty_waf = require "waf"
    
                                local waf = lua_resty_waf:new()
    
                                waf:exec()
                        ';
    
                        log_by_lua '
                                local lua_resty_waf = require "waf"
    
                                local waf = lua_resty_waf:new()
    
                                -- write out any event log entries to the
                                -- configured target, if applicable
                                waf:write_log_events()
                        ';
                }
        }

##公共函数

###lua_resty_waf.default_option()

为将在所有范围内继承的配置选项定义默认值。当您在许多不同的范围内(例如,许多服务器块、位置等)使用 lua-resty-waf 时,这很有用,并且您不想重复多次调用 set_option。如果您没有更改选项的值(与定义为默认值的值相同),则不必调用此函数。

        http {
                init_by_lua '
                        local lua_resty_waf = require "waf"
    
                        lua_resty_waf.default_option("debug", true)
    
                        -- this would be a useless operation since it does not change the default
                        lua_resty_waf.default_option("debug_log_level", ngx.INFO)
                ';
        }

###lua-resty-waf.init()

根据默认分发的规则集以及通过 default_option 添加或忽略的规则集,对规则和规则集执行一些预计算。建议,但不是必需,调用此函数(不这样做会导致轻微的性能损失)。此函数应在 init_by_lua 中的任何 lua-resty-waf 函数调用之后调用,并且永远不应该在此范围之外调用。

示例:

        http {
                init_by_lua '
                        local lua_resty_waf = require "waf"
    
                        -- set default options...
    
                        lua_resty_waf.init()
                ';
        }

##公共方法

###lua-resty-waf:new()

实例化 lua-resty-waf 的新实例。您必须在您希望运行 lua-resty-waf 的每个请求处理程序阶段调用此函数,并使用返回结果来调用进一步的类方法。

示例:

        location / {
                access_by_lua '
                        local lua_resty_waf = require "waf"
    
                        local waf = lua_resty_waf:new()
                ';
        }

###lua-resty-waf:set_option()

在每个范围内配置一个选项。只有当您要覆盖此范围内的默认值时才应该这样做(例如,在所有地方使用它来定义相同的可配置值是无用的)。

示例:

        location / {
                access_by_lua '
                        local lua_resty_waf = require "waf"
    
                        local waf = lua_resty_waf:new()
    
                        -- enable debug logging only for this scope
                        waf:set_option("debug", true)
                ';
        }

###lua-resty-waf:reset_option()

将给定选项设置为其记录的默认值,而不管通过 default_option 分配了哪个值。这对于比布尔值或整数值更复杂的选项最有用。

示例:

        http {
                init_by_lua '
                        local lua_resty_waf = require "waf"
    
                        lua_resty_waf.default_option("allowed_content_types", "text/json")
                ';
        }
    
        [...snip...]
    
        location / {
                access_by_lua '
                        local lua_resty_waf = require "waf"
    
                        local waf = lua_resty_waf:new()
    
                        -- reset the value to its documented default
                        waf:reset_option("allowed_content_types")
                ';
        }

###lua-resty-waf:write_log_events()

写入从事务中生成的任何审计日志条目。这应该在 log_by_lua 处理程序中调用。

示例:

        location / {
                log_by_lua '
                        local lua_resty_waf = require "waf"
    
                        local waf = lua_resty_waf:new()
    
                        -- write out any event log entries to the
                        -- configured target, if applicable
                        waf:write_log_events()
                ';
        }

##选项

可以使用 default_optionset_option 函数配置模块选项。在 init_by_lua 处理程序中使用 default_option,并且不调用 lua-resty-waf:new() 来设置将在所有范围内继承的默认值。这些值(或未被 default_option 修改的选项)可以在每个范围内通过 set_option 进行进一步调整。此外,范围级别选项可以通过 reset_option 方法重新调整回记录的默认值。这将把给定选项设置为其记录的默认值,覆盖由 default_option 函数设置的默认值。

请注意,在早期阶段处理程序中设置的选项不需要在后面的阶段重新设置,但可以被覆盖(即,您可以在 access 阶段设置 debug,但在 header_filter 中禁用它。有关可用选项的详细信息,请参见下文。

###add_ruleset

默认值:无

添加要用于处理的附加规则集。这允许用户实现自定义规则集,而不会覆盖包含的规则目录。附加规则集必须位于名为“rules”的文件夹中,该文件夹位于 lua_package_path 中。

示例:

        http {
                -- the lua module 50000.lua must live at
                -- /path/to/extra/rulesets/rules/50000.lua
                lua_package_path '/path/to/extra/rulesets/?.lua;;';
        }
    
        location / {
                access_by_lua '
                        waf:set_option("add_ruleset", "50000_extra_rules")
                ';
        }

可以通过将值表传递给 set_option 来添加多个规则集。请注意,规则集名称在处理之前会进行排序。规则集以低到高的排序顺序进行处理。

###add_ruleset_string

默认值:无

添加要用于处理的附加规则集。这允许用户实现自定义规则集,而不会覆盖包含的规则目录。规则集以 Lua 字符串的形式内联定义,形式为已翻译的规则集 JSON 结构。

示例:

        location / {
                access_by_lua '
                        waf:set_option("add_ruleset_string", "70000_extra_rules", [=[{"access":[{"action":"DENY","id":73,"operator":"REGEX","opts":{},"pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
                ';
        }

请注意,规则集名称在处理之前会进行排序,并且必须以字符串的形式给出。规则集以低到高的排序顺序进行处理。

###allow_unknown_content_types

默认值:false

指示 lua-resty-waf 在发送了不在 allowed_content_types 表中的 Content-Type 标头时继续处理请求。此类请求不会由 lua-resty-waf 处理其请求主体(REQUEST_BODY 集合将为 nil)。通过这种方式,用户无需显式白名单他们可能遇到的所有可能的 Content-Type 标头。

示例:

        location / {
                access_by_lua '
                        waf:set_option("allow_unknown_content_types", true)
                ';
        }

###allowed_content_types

默认值:无

定义一个或多个允许的 Content-Type 标头,除了默认的 Content-Type application/x-www-form-urlencodedmultipart/form-data 之外。其内容类型与 allowed_content_types 中的某个值匹配的请求将把 REQUEST_BODY 集合设置为包含单个字符串(而不是表);其内容类型与这些值之一或 application/x-www-form-urlencodedmultipart/form-data 不匹配的请求将被拒绝。

示例:

        location / {
                access_by_lua '
                        -- define a single allowed Content-Type value
                        waf:set_option("allowed_content_types", "text/xml")
    
                        -- defines multiple allowed Content-Type values
                        waf:set_option("allowed_content_types", { "text/html", "text/json", "application/json" })
                ';
        }

请注意,使用 allowed_content_types 参数的多个 set_option 调用将简单地覆盖现有的选项表,因此如果您要定义多个允许的内容类型,则必须将它们定义为 Lua 表,如上所示。

###debug

默认值:false

禁用/启用调试日志记录。调试日志语句打印到 error_log。请注意,调试日志记录非常昂贵,不应在生产环境中使用。

示例:

        location / {
                access_by_lua '
                        waf:set_option("debug", true)
                ';
        }

###debug_log_level

默认值:ngx.INFO

设置用于调试日志记录的 nginx 日志级别常量。

示例:

        location / {
                access_by_lua '
                        waf:set_option("debug_log_level", ngx.DEBUG)
                ';
        }

###deny_status

默认值:ngx.HTTP_FORBIDDEN

设置拒绝请求时要使用的状态。

示例:

        location / {
                access_by_lua '
                        waf:set_option("deny_status", ngx.HTTP_NOT_FOUND)
                ';
        }

###disable_pcre_optimization

默认值:false

从所有 ngx.re.matchngx.re.findngx.re.sub 调用中删除 oj 标志。这可能在使用旧版 PCRE 库的某些情况下有用,但会导致严重的性能下降,因此强烈不建议使用;相反,用户鼓励使用现代的、支持 JIT 的 PCRE 库构建 OpenResty。

示例:

        location / {
                access_by_lua '
                        waf:set_option("disable_pcre_optimization", true)
                ';
        }

###event_log_altered_only

默认值:true

确定是否为未被 lua-resty-waf 修改的事务中的规则匹配写入日志条目。“修改”的定义是 lua-resty-waf 对其操作为 ACCEPTDENY 的规则采取行动。当此选项未设置时,即使事务未被修改,lua-resty-waf 也会记录规则匹配。默认情况下,lua-resty-waf 仅在事务被修改时才会为匹配项写入日志条目。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_altered_only", false)
                ';
        }

请注意,mode 不会影响判断交易是否被修改。也就是说,如果匹配到具有 DENY 操作的规则,但 lua-resty-waf 在 SIMULATE 模式下运行,该交易仍将被视为已修改,并且将记录规则匹配。

###event_log_buffer_size

默认: 4096

定义用于保存事件日志的缓冲区的阈值大小(以字节为单位)。当达到此阈值时,将刷新缓冲区。

示例:

        location / {
                access_by_lua '
                        -- 8 KB event log message buffer
                        waf:set_option("event_log_buffer_size", 8192)
                ';
        }

###event_log_level

默认值:ngx.INFO

设置用于事件日志记录的 nginx 日志级别常量。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_level", ngx.WARN)
                ';
        }

###event_log_ngx_vars

默认:空

定义从 ngx.var 中将哪些额外的变量放入日志事件中。这是一种将警报与额外上下文扩展的通用方法。变量名称将是日志条目中 ngx 键下的条目的键。如果变量不存在为 nginx 变量,则不会将任何项目添加到事件中。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_ngx_vars", "host")
                        waf:set_option("event_log_ngx_vars", "request_id")
                ';
        }

生成的事件具有以下额外项目

    {
        "ngx": {
                "host": "example.com",
                "request_id": "373bcce584e3c18a"
        }
    }

###event_log_periodic_flush

默认值:无

定义事件日志缓冲区定期刷新的时间间隔(以秒为单位)。如果没有配置任何值,缓冲区将不会定期刷新,而只会当达到 event_log_buffer_size 阈值时才刷新。为可能在很长一段时间内未收到任何事件日志数据的流量非常低的站点配置此选项,以防止过时数据停留在缓冲区中。

示例:

        location / {
                access_by_lua '
                        -- flush the event log buffer every 30 seconds
                        waf:set_option("event_log_periodic_flush", 30)
                ';
        }

###event_log_request_arguments

默认值:false

当设置为 true 时,日志条目包含 uri_args 键下的请求参数。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_request_arguments", true)
                ';
        }

###event_log_request_body

默认值:false

当设置为 true 时,日志条目包含 request_body 键下的请求正文。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_request_arguments", true)
                ';
        }

###event_log_request_headers

默认值:false

HTTP 请求的标头将被复制到日志事件中,位于 request_headers 键下。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_request_headers", true)
                ';
        }

生成的事件具有以下额外项目

    {
        "request_headers": {
                "accept": "*/*",
                "user-agent": "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3"
        }
    }

###event_log_ssl

默认值:false

通过 TCP/UDP 日志记录时启用 SSL 连接。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_ssl", true)
                ';
        }

###event_log_ssl_sni_host

默认值:无

lua-resty-logger-socket 连接设置 SNI 主机。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_ssl_sni_host", "loghost.example.com")
                ';
        }

###event_log_ssl_verify

默认值:false

通过 TCP/UDP 日志记录时启用 SSL 连接的证书验证。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_ssl_verify", true)
                ';
        }

###event_log_socket_proto

默认:udp

定义通过远程套接字发送事件日志时要使用的 IP 协议(TCP 或 UDP)。无论协议如何,都将使用相同的缓冲和循环刷新逻辑。

示例:

        location / {
                access_by_lua '
                        -- send logs via TCP
                        waf:set_option("event_log_socket_proto", "tcp")
                ';
        }

###event_log_target

默认:error

定义事件日志的目标。lua-resty-waf 目前支持将日志记录到错误日志、本地文件系统上的单独文件或远程 TCP 或 UDP 服务器。在后两种情况下,事件日志将被缓冲,并在达到定义的阈值时刷新(有关事件日志记录选项的更多选项,请参见下面的内容)。

示例:

        location / {
                access_by_lua '
                        -- send event logs to the server's error_log location (default)
                        waf:set_option("event_log_target", "error")
    
                        -- send event logs to a local file on disk
                        waf:set_option("event_log_target", "file")
    
                        -- send event logs to a remote server
                        waf:set_option("event_log_target", "socket")
                ';
        }

请注意,由于使用的日志记录库中的限制,只能定义一个目标套接字(和单独的文件目标)。也就是说,您可以选择在不同位置同时使用套接字和文件日志记录,但您只能配置一个具有特定主机/端口组合的 socket 目标;如果您配置第二个主机/端口组合,数据将无法正确记录。类似地,如果您使用 file 日志记录目标,您只能定义一个文件路径;写入第二个路径位置的数据将丢失。

###event_log_target_host

默认值:无

为目标远程服务器的事件日志定义目标服务器。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_target_host", "10.10.10.10")
                ';
        }

###event_log_target_path

默认值:无

为目标本地文件系统位置的事件日志定义目标路径。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_target_path", "/var/log/lua-resty-waf/event.log")
                ';
        }

此路径必须位于 nginx 用户可写入的位置。请注意,从本质上讲,磁盘上的日志记录会导致高并发环境中的性能明显下降。

###event_log_target_port

默认值:无

为目标远程服务器的事件日志定义目标端口。

示例:

        location / {
                access_by_lua '
                        waf:set_option("event_log_target_port", 9001)
                ';
        }

###event_log_verbosity

默认: 1

设置用于写入事件日志通知的详细程度。详细程度越高,为每个通知生成的 JSON 块中包含的信息就越多。

示例:

        location / {
                access_by_lua '
                        -- default verbosity. the client IP, request URI, rule match data, and rule ID will be logged
                        waf:set_option("event_log_verbosity", 1)
    
                        -- the rule description will be written in addition to existing data
                        waf:set_option("event_log_verbosity", 2)
    
                        -- the rule description, options and action will be written in addition to existing data
                        waf:set_option("event_log_verbosity", 3)
    
                        -- the entire rule definition, including the match pattern, will be written in addition to existing data
                        -- note that for some rule definitions, such as the XSS and SQLi rulesets, this pattern can be large
                        waf:set_option("event_log_verbosity", 4)
                ';
        }

###hook_action

默认值:无

覆盖匹配规则时执行的操作的功能。有关更多详细信息,请参见示例。

示例:

                location / {
                        access_by_lua '
                                local deny_override = function(waf, ctx)
                                        ngx.log(ngx.INFO, "Overriding DENY action")
                                        ngx.status = 404
                                end
    
                                -- override the DENY action with the function defined above
                                waf:set_option("hook_action", "DENY", deny_override)
                        ';
                }

###ignore_rule

默认值:无

指示模块忽略指定的规则 ID。请注意,忽略链中的规则会导致整个链被忽略,处理将继续到链后的下一条规则。

示例:

        location / {
                access_by_lua '
                        waf:set_option("ignore_rule", 40294)
                ';
        }

可以通过将规则 ID 表传递给 set_option 来忽略多条规则。

###ignore_ruleset

默认值:无

指示模块忽略整个规则集。当某些规则集(例如 SQLi 或 XSS CRS 规则集)太容易产生误报或不适用于您的应用程序时,这可能很有用。

示例:

        location / {
                access_by_lua '
                        waf:set_option("ignore_ruleset", "41000_sqli")
                ';
        }

###mode

默认:SIMULATE

设置模块的操作模式。选项为 ACTIVE、INACTIVE 和 SIMULATE。在 ACTIVE 模式下,记录规则匹配并运行操作。在 SIMULATE 模式下,lua-resty-waf 将循环遍历每个启用的规则并记录规则匹配,但不会完成给定运行中指定的动作。INACTIVE 模式会阻止模块运行。

默认情况下,如果未明确设置模式,将选择 SIMULATE;这要求新用户通过将模式设置为 ACTIVE 来主动实现阻止。

示例:

        location / {
                access_by_lua '
                        waf:set_option("mode", "ACTIVE")
                ';
        }

###nameservers

默认值:无

设置用于 RBL 查找的 DNS 解析器。目前仅支持 UDP/53 流量。此选项必须定义为数字地址,而不是主机名。如果未定义此选项,所有 RBL 查找规则将返回 false。

示例:

        location / {
                access_by_lua '
                        waf:set_option("nameservers", "10.10.10.10")
                ';
        }

###process_multipart_body

默认 true

启用使用 lua-resty-upload 模块处理 multipart/form-data 请求正文(如果存在)。将来,lua-resty-waf 可能会使用此处理来对上传正文执行更严格的检查;目前,此模块仅对请求正文执行最小的健全性检查,如果请求正文无效,将不会记录事件。如果您不需要此检查,或者上游模块中的错误导致 HTTP 上传出现问题,请禁用此选项。

示例:

        location / {
                access_by_lua '
                        -- disable processing of multipart/form-data requests
                        -- note that the request body will still be sent to the upstream
                        waf:set_option("process_multipart_body", false)
                ';
        }

###req_tid_header

默认值:false

在上游请求中设置 HTTP 标头 X-Lua-Resty-WAF-ID,其值为事务 ID。此 ID 将与调试日志中存在的事务 ID 相对应(如果设置)。这对于请求跟踪或调试目的可能很有用。

示例:

        location / {
                access_by_lua '
                        waf:set_option("req_tid_header", true)
                ';
        }

###res_body_max_size

默认:1048576(1 MB)

定义响应正文将不会被处理的内容长度阈值。响应正文的大小由 Content-Length 响应标头确定。如果响应中不存在此标头,则响应正文将永远不会被处理。

示例:

        location / {
                access_by_lua '
                        -- increase the max response size to 2 MB
                        waf:set_option("res_body_max_size", 1024 * 1024 * 2)
                ';
        }

请注意,从本质上讲,需要缓冲整个响应正文才能正确使用响应作为集合,因此不建议在没有充分理由的情况下(以及充足的服务器资源)大幅增加此数字。

###res_body_mime_types

默认:"text/plain","text/html"

定义 lua-resty-waf 将使用其处理响应正文的 MIME 类型。此值由 Content-Type 标头确定。如果此标头不存在,或者响应类型不在此列表中,则响应正文将不会被处理。设置此选项将向现有的 text/plaintext/html 默认值添加给定的 MIME 类型。

示例:

        location / {
                access_by_lua '
                        -- mime types that will be processed are now text/plain, text/html, and text/json
                        waf:set_option("res_body_mime_types", "text/json")
                ';
        }

可以通过将类型表传递给 set_option 来添加多个 MIME 类型。

###res_tid_header

默认值:false

在下游响应中设置 HTTP 标头 X-Lua-Resty-WAF-ID,其值为事务 ID。此 ID 将与调试日志中存在的事务 ID 相对应(如果设置)。这对于请求跟踪或调试目的可能很有用。

示例:

        location / {
                access_by_lua '
                        waf:set_option("res_tid_header", true)
                ';
        }

###score_threshold

默认: 5

设置异常评分的阈值。当达到阈值时,lua-resty-waf 将拒绝请求。

示例:

        location / {
                access_by_lua '
                        waf:set_option("score_threshold", 10)
                ';
        }

###storage_backend

默认:dict

定义一个用于持久变量存储的引擎。当前可用的选项是 dict(ngx_lua 共享内存区域)、memcachedredis

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_backend", "memcached")
                ';
        }

###storage_keepalive

默认值:true

启用或禁用对远程持久存储主机的连接的 TCP 保持活动状态。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_keepalive", false)
                ';
        }

###storage_keepalive_timeout

默认: 10000

配置(以毫秒为单位)远程持久存储主机的 cosocket 保持活动状态池的超时时间。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_keepalive_timeout", 30000)
                ';
        }

###storage_keepalive_pool_size

默认: 100

配置远程持久存储主机的 cosocket 保持活动状态池的池大小。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_keepalive_pool_size", 50)
                ';
        }

###storage_memcached_host

默认: 127.0.0.1

定义在使用 memcached 作为持久变量存储引擎时要使用的主机。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_host", "10.10.10.10")
                ';
        }

###storage_memcached_port

默认: 11211

定义在使用 memcached 作为持久变量存储引擎时要使用的端口。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_port", 11221)
                ';
        }

###storage_redis_host

默认: 127.0.0.1

定义在使用 redis 作为持久变量存储引擎时要使用的主机。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_host", "10.10.10.10")
                ';
        }

###storage_redis_port

默认: 6379

定义在使用 redis 作为持久变量存储引擎时要使用的端口。

示例:

        location / {
                acccess_by_lua '
                        waf:set_option("storage_port", 6397)
                ';
        }

###storage_zone

默认值:无

定义将用于保存持久存储数据的 lua_shared_dict。此区域必须在配置的 http{} 块中定义。

示例:_

        http {
                -- define a 64M shared memory zone to hold persistent storage data
                lua_shared_dict persistent_storage 64m;
        }
    
        location / {
                access_by_lua '
                        waf:set_option("storage_zone", "persistent_storage")
                ';
        }

可以定义和使用多个共享区域,但每个配置位置只能定义一个区域。如果区域已满并且共享字典接口无法添加更多键,则以下内容将被记录到错误日志中

错误将键添加到持久存储中,请增加 lua_shared_dict 的大小

##阶段处理

lua-resty-waf 旨在在请求生命周期的多个阶段运行。可以在以下阶段处理规则

  • access:在此阶段可以使用请求信息,例如 URI、请求标头、URI 参数和请求正文。

  • header_filter:在此阶段可以使用响应标头和 HTTP 状态。

  • body_filter:在此阶段可以使用响应正文。

这些阶段对应于它们各自的 Nginx lua 处理程序(分别为 access_by_luaheader_filter_by_luabody_filter_by_lua)。请注意,在不在此列表中的 lua 阶段处理程序中运行 lua-resty-waf 会导致行为出现问题。在较晚阶段可以使用在较早阶段提供的所有数据。也就是说,在 access 阶段提供的数据也将在 header_filterbody_filter 阶段提供,但反之则不行。

此外,需要在 log_by_lua 处理程序中调用 write_log_events。lua-resty-waf 并非旨在在此阶段处理规则;在请求后期记录规则允许将所有规则合并到每个请求的单个条目中。有关示例语法,请参见上面的概要。

##包含的规则集

lua-resty-waf 与许多规则集一起分发,这些规则集旨在模拟 ModSecurity CRS 的功能。为了参考,这些规则集列在这里

  • 11000_whitelist:本地策略白名单

  • 20000_http_violation:HTTP 协议违规

  • 21000_http_anomaly:HTTP 协议异常

  • 35000_user_agent:恶意/可疑用户代理

  • 40000_generic_attack:通用攻击

  • 41000_sqli:SQLi

  • 42000_xss:XSS

  • 90000_custom:自定义规则/虚拟补丁

  • 99000_scoring:异常评分处理

##规则定义

lua-resty-waf 从存储在磁盘上的 JSON 块中解析规则定义。规则根据目的和严重程度进行分组,定义为规则集。包含的规则集旨在模拟 ModSecurity CRS 的某些功能,特别是 base_rules 定义。此外,包含的 modsec2lua-resty-waf.pl 脚本可用于将其他或自定义规则集转换为 lua-resty-waf 兼容的 JSON 块。

请注意,转换脚本在不支持的操作、集合和运算符方面存在一些限制。请参见 此维基页面,了解已知不兼容性的最新列表。

##注释

###社区

有一个 Freenode IRC 频道 #lua-resty-waf。Travis CI 会在此处发送通知;请随时在此频道中提出问题/留下评论。

此外,CodeWake 提供问答

[!Codewake](https://www.codewake.com/p/lua-resty-waf)

###拉取请求

请将所有拉取请求针对开发分支,或针对功能分支(如果 PR 是重大更改)。对 master 的提交应仅以文档更新或对模块本身没有影响的其他更改(并且可以干净地合并到 development 中)的形式出现。

##路线图

  • 扩展的虚拟补丁规则集:增加对新兴威胁的覆盖范围。

  • 扩展的集成/验收测试:增加对常见威胁和使用场景的覆盖范围。

  • 扩展的 ModSecurity 语法翻译:支持更多运算符、变量和操作。

  • 常用应用程序配置文件:针对常见 CMS/应用程序的调整规则集。

  • 支持多个套接字/文件记录器目标:可能需要分叉 lua-resty-logger-socket 项目。

## 限制

lua-resty-waf 正在不断开发和改进,因此其功能和性能可能有限。目前已知的限制可以在此仓库的 GitHub 问题跟踪器中找到。

## 许可证

本程序是免费软件:您可以在自由软件基金会发布的 GNU 通用公共许可证的条款下重新发布和/或修改它,无论是许可证的版本 3 还是(根据您的选择)任何更高版本。

本程序按“原样”分发,不提供任何形式的保证;甚至不包括适销性或适合特定用途的默示保证。有关更多详细信息,请参阅 GNU 通用公共许可证。

您应该已经收到一份 GNU 通用公共许可证副本,与本程序一起。如果没有,请参见 <https://gnu.ac.cn/licenses/>

## 错误

请通过在 GitHub 问题跟踪器中创建工单来报告错误。

## 另请参阅

  • OpenResty 项目:<https://openresty.org.cn/>

  • 我个人博客,用于更新和记录 lua-resty-waf 开发:<http://www.cryptobells.com/tag/lua-resty-waf/>

作者

Robert Paprocki (p0pr0ck5)

许可

gpl3

版本