基于OpenResty的WAF构建笔记


这是我在学习构建WAF时记录的一些信息,包含我学习时的代码。我学习的项目《loveshell/ngx_lua_waf》,感谢神奇的魔法师开源了这么好的东西。
我的目的是构建一个高性能WAF。我将使用OpenResty与Redis来构建。后期可能会对WAF进行性能优化,因此这不是终稿。

置顶备忘:未处理BUG

已知问题:

  1. 《openresty的unescape_uri函数处理百分号后面字符的小特性》 未解决
  2. init.lua中检测POST文件时只能获取第一个文件的后缀名,应当支持获取所有文件后缀名 未解决
  3. init.lua中POST检查x-www-form-urlencoded表单时,只检测了一维数组,二维数组,应当支持更多维数组的检查 未解决
  4. 性能提升:ngx.re.match()改用ngx.re.find() 未解决
  5. CDN场景下,CC攻击应该添加CDN厂家IP白名单,需要修改init.lua中的isCCAttack()函数 未解决

环境与目录配置

在/usr/local/openresty/nginx/conf/新建文件夹waf存储WAF相关文件

mkdir /usr/local/openresty/nginx/conf/waf 
touch /usr/local/openresty/nginx/conf/waf/init.lua /usr/local/openresty/nginx/conf/waf/main.lua /usr/local/openresty/nginx/conf/waf/config.lua 

WAF目录结构如下

waf/
├── config.lua
├── init.lua
├── main.lua

编辑/usr/local/openresty/nginx/conf/nginx.conf

vim /usr/local/openresty/nginx/conf/nginx.conf

/usr/local/openresty/nginx/conf/nginx.conf

#user  nobody;
worker_processes  1;

error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    
    
    lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua;/usr/local/openresty/lualib/?.lua;;";
    lua_shared_dict limit 10m;
    init_by_lua_file  /usr/local/openresty/nginx/conf/waf/init.lua; 
    access_by_lua_file /usr/local/openresty/nginx/conf/waf/main.lua;

}

新增代码如下:

lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua;/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict limit 10m;
init_by_lua_file  /usr/local/openresty/nginx/conf/waf/init.lua; 
access_by_lua_file /usr/local/openresty/nginx/conf/waf/main.lua;

lua_package_path声明luapath环境变量。
lua_shared_dict声明一个共享内存区域 name,以充当基于 Lua 字典 ngx.shared.<name> 的共享存储。共享内存总是被当前 Nginx 服务器实例中所有的 Nginx worker 进程所共享。
init_by_lua_file声明当 nginx master 进程在加载 nginx 配置文件时运行指定的 lua 脚本,用来注册 lua 的全局变量或在服务器启动时预加载 lua 模块。
access_by_lua_file声明一个脚本在访问阶段对每个请求进行处理。主要用于访问控制,能收集到大部分的变量。

Redis设置

设置密码

为了安全,WAF默认开启redis密码。
编辑/etc/redis.conf
更改#requirepass foobared为requirepass password

WAF的结构

s=>start: 开始
e=>end: 请求通过
respose403=>operation: 返回403(禁止访问)
respose503=>operation: 返回503(服务器忙)
respose444=>operation: 返回444(空回应)
isWhiteIp=>condition: 是否是白名单IP?
isBlackIp=>condition: 是否是黑名单IP?
isCcAttack=>condition: 是否是CC攻击?
isScanner=>condition: 是否是扫描器?
isWhiteUri=>condition: 是否是白名单URI?
isBlackUa=>condition: 是否是黑名单UA?
isBlackUri=>condition: 是否是黑名单URI?
isBlackGetArgs=>condition: GET请求中是否有恶意参数?
isBlackCookie=>condition: Cookie中是否有恶意参数?
isBlackPostArgs=>condition:  POST请求中是否有恶意参数?

s->isWhiteIp->isBlackIp->isCcAttack->isScanner->isWhiteUri->isBlackUa->isBlackUri->isBlackGetArgs->isBlackCookie->isBlackPostArgs->e

isWhiteIp(yes)->e
isWhiteIp(no)->isBlackIp
isBlackIp(yes)->respose403
isBlackIp(no)->isCcAttack
isCcAttack(yes)->respose503
isCcAttack(no)->isScanner
isScanner(yes)->respose444
isScanner(no)->isWhiteUri
isWhiteUri(yes)->e
isWhiteUri(no)->isBlackUa
isBlackUa(yes)->respose403
isBlackUa(no)->isBlackUri
isBlackUri(yes)->respose403
isBlackUri(no)->isBlackGetArgs
isBlackGetArgs(yes)->respose403
isBlackGetArgs(no)->isBlackCookie
isBlackCookie(yes)->respose403
isBlackCookie(no)->isBlackPostArgs
isBlackPostArgs(yes)->respose403
isBlackPostArgs(no)->e

以上即是我学习构建WAF的笔记,我的邮箱是wubo@wubo.net.cn,欢迎和我讨论。

声明:物博网|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 基于OpenResty的WAF构建笔记


喜欢安全与WEB开发