文章总结: 本文阐述基于OpenResty与Lua设计简单WAF的架构,涵盖项目目录结构及Nginx核心配置详解。重点分析worker进程、MIME类型、Lua脚本挂载点及共享内存字典配置,深入解析access、body_filter等阶段实现请求检查与响应过滤。同时说明反向代理设置及常用Nginx指令含义,为后续Lua规则引擎开发与测试奠定基础,指导开发者构建基础Web应用防护系统。 综合评分: 84 文章分类: 安全开发,安全工具,WEB安全
安全开发设计一个简单的waf(一)
原创
pr0phet
千兆安全
2025年12月24日 19:22 山西
在看文章前,建议先得有点预备知识,以下三点必须学会初步运用
- lua
- openresty
- nginx
项目结构
simplewaf/├── conf/│ ├── nginx.conf # OpenResty主配置│ ├── waf-config.json # WAF主配置│ ├── mime.types # MIME类型配置│ └── rules/│ ├── sql-injection.json # SQL注入规则│ ├── xss.json # XSS规则│ ├── path-traversal.json # 路径遍历规则│ ├── command-injection.json # 命令注入规则│ └── bad-bots.json # 恶意爬虫规则├── lua/│ ├── simplewaf.lua # 主入口模块│ ├── simplewaf-admin.lua # 管理接口模块(新增)│ ├── rule-engine.lua # 规则引擎│ ├── request-processor.lua # 请求处理器│ ├── response-processor.lua # 响应处理器│ ├── logging.lua # 日志模块│ ├── ip-blacklist.lua # IP黑名单│ ├── utils.lua # 工具函数│ └── resty/ # 第三方库目录│ └── README.md # 库说明├── html/│ ├── block.html # 拦截页面│ └── index.html # 测试页面├── logs/ # 日志目录(运行时生成)├── docker/│ ├── Dockerfile # Docker容器配置│ └── docker-compose.yml # Docker Compose配置├── test/│ ├── test-attacks.lua # 攻击测试脚本│ ├── test-waf.lua # WAF功能测试│ └── test-data/│ └── sample-attacks.txt # 测试攻击载荷└── README.md # 项目说明文档
首先看openresty的主配置–nginx.conf
worker_processes auto;error_log logs/error.log warn;pid logs/nginx.pid;events { worker_connections 1024;}http { # 基础配置 include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # Lua包路径 lua_package_path "$prefix/lua/?.lua;;"; lua_package_cpath "$prefix/lib/?.so;;"; # 共享内存字典(用于IP黑名单等) lua_shared_dict waf_ip_blacklist 10m; lua_shared_dict waf_request_counter 10m; # 初始化阶段 init_by_lua_block { -- 加载WAF主模块 require("simplewaf").init() } # 初始化worker init_worker_by_lua_block { require("simplewaf").init_worker() } # 主服务器配置 server { listen 80; server_name localhost; # WAF检查阶段 access_by_lua_block { local waf = require("simplewaf") waf.check_request() } # 内容处理阶段(可选响应检查) body_filter_by_lua_block { local waf = require("simplewaf") waf.check_response() } # 日志阶段 log_by_lua_block { local waf = require("simplewaf") waf.log_request() } # 默认后端应用(模拟被保护的应用) location / { proxy_pass http://backend_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # WAF管理接口 location /waf-admin { access_by_lua_block { -- 简单的管理认证 local auth = ngx.req.get_headers()["X-WAF-Admin-Token"] if auth ~= "your-secret-token" then ngx.exit(403) end } content_by_lua_block { require("simplewaf.admin").handle_request() } } # 拦截页面 location /waf-block { internal; default_type text/html; content_by_lua_block { ngx.say(require("simplewaf").get_block_page()) } } } # 后端应用(模拟) upstream backend_app { server 127.0.0.1:8080; }}
这里就设计到nginx的配置,这里简单说下都是什么意思
worker_processes auto;设置值为auto,表示设置 Nginx 工作进程数根据cpu核心数来自动设置
error_log logs/error.log warn;错误日志为 logs/error.log,日志告警级别为warn
pid logs/nginx.pid;指定 Nginx 的进程 ID 文件路径为 logs/nginx.pid
events { worker_connections 1024;}每个进程设置连接数最大为1024
再看http模块
首先设置mime类型,根据文件后缀设置对应的Content-type,默认的mime类型为application/octet-stream
types { text/html html htm shtml; text/css css; text/xml xml; image/gif gif; image/jpeg jpeg jpg; application/javascript js; application/atom+xml atom; application/rss+xml rss;
text/mathml mml; text/plain txt; text/vnd.sun.j2me.app-descriptor jad; text/vnd.wap.wml wml; text/x-component htc;
image/png png; image/svg+xml svg svgz; image/tiff tif tiff; image/vnd.wap.wbmp wbmp; image/webp webp; image/x-icon ico; image/x-jng jng; image/x-ms-bmp bmp;
font/woff woff; font/woff2 woff2;
application/java-archive jar war ear; application/json json; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; application/postscript ps eps ai; application/rtf rtf; application/vnd.apple.mpegurl m3u8; application/vnd.ms-excel xls; application/vnd.ms-fontobject eot; application/vnd.ms-powerpoint ppt; application/vnd.wap.wmlc wmlc; application/vnd.google-earth.kml+xml kml; application/vnd.google-earth.kmz kmz; application/x-7z-compressed 7z; application/x-cocoa cco; application/x-java-archive-diff jardiff; application/x-java-jnlp-file jnlp; application/x-makeself run; application/x-perl pl pm; application/x-pilot prc pdb; application/x-rar-compressed rar; application/x-redhat-package-manager rpm; application/x-sea sea; application/x-shockwave-flash swf; application/x-stuffit sit; application/x-tcl tcl tk; application/x-x509-ca-cert der pem crt; application/x-xpinstall xpi; application/xhtml+xml xhtml; application/xspf+xml xspf; application/zip zip;
application/octet-stream bin exe dll; application/octet-stream deb; application/octet-stream dmg; application/octet-stream iso img; application/octet-stream msi msp msm;
audio/midi mid midi kar; audio/mpeg mp3; audio/ogg ogg; audio/x-m4a m4a; audio/x-realaudio ra;
video/3gpp 3gpp 3gp; video/mp2t ts; video/mp4 mp4; video/mpeg mpeg mpg; video/quicktime mov; video/webm webm; video/x-flv flv; video/x-m4v m4v; video/x-mng mng; video/x-ms-asf asx asf; video/x-ms-wmv wmv; video/x-msvideo avi;}
sendfile开启,允许操作系统直接从文件系统将文件内容传输到网络缓冲区keepalive_timeout长连接超时时间
server模块大部分我都写了注解,重点说一下代理方面的
location / { proxy_pass http://backend_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
这段配置的作用是:
- 将所有对 Nginx 服务器的请求(路径为 /)代理到后端应用服务器 backend_app。
- 在代理请求时,设置请求头 Host 为客户端请求的主机名,确保后端服务器能够正确识别请求的主机名。
- 设置请求头 X-Real-IP 为客户端的真实 IP 地址,确保后端服务器能够获取客户端的真实 IP 地址。
upstream backend_app { server 127.0.0.1:8080; }
后端应用组一般搭配proxy_pass一起用,常用于负载均衡
配置中还有设置的一些模块如下
set_by_lua:设置变量; rewrite_by_lua:转发、重定向等; access_by_lua:准入、权限等; content_by_lua:生成返回内容; header_filter_by_lua:应答头过滤处理; body_filter_by_lua:应答体过滤处理; log_by_lua:日志记录。
这些主要搭配lua脚本执行
下期将开始讲解lua脚本,把核心处理逻辑脚本过一遍,最后一期编写测试用例和规则
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:千兆安全 pr0phet《安全开发设计一个简单的waf(一)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论