Nginx结合Lua实现WAF拦截并可视化配置教程框架。搭建基础Web应用防火墙(WAF)系统。
1. 什么是WAF以及为什么选择Nginx+Lua
Web应用防火墙(WAF)是一种特殊的网络安全解决方案,用于保护Web应用免受各种攻击,如SQL注入、XSS、CSRF等。Nginx是一个高性能的Web服务器和反向代理服务器,而Lua是一种轻量级脚本语言。通过Lua模块,我们可以在Nginx中编写自定义逻辑,实现灵活的WAF功能。
2. 环境准备
首先需要安装Nginx和Lua模块。推荐使用OpenResty,它是一个基于Nginx的高性能Web平台,集成了LuaJIT和一系列Lua模块。
# 安装OpenResty
wget https://openresty.org/download/openresty-1.21.4.1.tar.gz
tar -xzvf openresty-1.21.4.1.tar.gz
cd openresty-1.21.4.1/
./configure
make
sudo make install
3. 基本WAF规则实现
下面是一个简单的基于Lua的WAF实现,用于检测和拦截常见的攻击模式:
-- waf.lua
-- 定义攻击特征匹配规则
local rules = {
-- SQL注入检测
{pattern = "('|%27).*(union|select|insert|update|delete)", description = "SQL注入检测"},
-- XSS攻击检测
{pattern = "<script.*?>.*?</script>", description = "XSS脚本攻击检测"},
{pattern = "<.*?on[a-z]+=.*?>", description = "XSS事件监听攻击检测"},
-- 命令注入检测
{pattern = "(;|&|%|%20|%0A).*(cat|ls|pwd|rm|mv|cp|echo)", description = "命令注入检测"},
}
-- 获取请求信息
local uri = ngx.var.uri
local args = ngx.req.get_uri_args()
local method = ngx.var.request_method
-- 检查URI
for _, rule in ipairs(rules) do
if string.find(uri, rule.pattern) then
ngx.log(ngx.ERR, "WAF拦截: ", rule.description, ", URI: ", uri)
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Access Denied")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
-- 检查GET参数
if method == "GET" then
for key, value in pairs(args) do
if type(value) == "string" then
for _, rule in ipairs(rules) do
if string.find(value, rule.pattern) then
ngx.log(ngx.ERR, "WAF拦截: ", rule.description, ", 参数: ", key, " = ", value)
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Access Denied")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
end
end
end
-- 检查POST参数
if method == "POST" then
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
for key, value in pairs(post_args) do
if type(value) == "string" then
for _, rule in ipairs(rules) do
if string.find(value, rule.pattern) then
ngx.log(ngx.ERR, "WAF拦截: ", rule.description, ", 参数: ", key, " = ", value)
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Access Denied")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
end
end
end
end
4. 配置Nginx加载WAF脚本
在Nginx配置文件中添加以下内容,让Nginx在处理请求时加载并执行WAF脚本:
# nginx.conf
http {
# ... 其他配置 ...
# Lua模块路径
lua_package_path "/path/to/your/lua/?.lua;;";
server {
listen 80;
server_name example.com;
# 在处理请求前执行WAF脚本
access_by_lua_file /path/to/your/waf.lua;
# ... 其他配置 ...
}
}
5. 实现可视化配置界面
为了便于管理WAF规则,我们可以创建一个简单的Web界面来管理规则。以下是一个基本的实现:
-- waf_manager.lua
local uri_args = ngx.req.get_uri_args()
local action = uri_args.action or ""
-- 规则文件路径
local rules_file = "/path/to/your/rules.lua"
-- 读取规则文件
local function read_rules()
local file = io.open(rules_file, "r")
if not file then
return {}
end
local content = file:read("*all")
file:close()
-- 解析规则文件
local rules = {}
local pattern = "{'([^']+)', '([^']+)', '([^']+)'}"
for description, pattern, action in string.gmatch(content, pattern) do
table.insert(rules, {
description = description,
pattern = pattern,
action = action
})
end
return rules
end
-- 写入规则文件
local function write_rules(rules)
local file = io.open(rules_file, "w")
if not file then
return false
end
for _, rule in ipairs(rules) do
file:write(string.format("{'%s', '%s', '%s'}\n",
rule.description, rule.pattern, rule.action))
end
file:close()
return true
end
-- 处理不同的请求动作
if action == "list" then
-- 返回规则列表
local rules = read_rules()
local json = require("cjson")
ngx.say(json.encode(rules))
elseif action == "add" then
-- 添加新规则
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
local new_rule = {
description = post_args.description or "",
pattern = post_args.pattern or "",
action = post_args.action or "block"
}
local rules = read_rules()
table.insert(rules, new_rule)
if write_rules(rules) then
ngx.say("规则添加成功")
else
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.say("规则添加失败")
end
elseif action == "delete" then
-- 删除规则
local index = tonumber(uri_args.index)
if not index then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.say("无效的规则索引")
return
end
local rules = read_rules()
if index < 1 or index > #rules then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.say("规则索引超出范围")
return
end
table.remove(rules, index)
if write_rules(rules) then
ngx.say("规则删除成功")
else
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.say("规则删除失败")
end
else
-- 默认显示管理界面
ngx.header.content_type = "text/html";
ngx.say([[
<!DOCTYPE html>
<html>
<head>
<title>WAF规则管理</title>
<style>
body { font-family: Arial, sans-serif; }
.container { max-width: 800px; margin: 0 auto; padding: 20px; }
table { width: 100%; border-collapse: collapse; }
table, th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f2f2f2; }
input, button { padding: 8px; margin: 5px 0; }
</style>
</head>
<body>
<div class="container">
<h2>WAF规则管理</h2>
<h3>添加新规则</h3>
<form action="?action=add" method="post">
描述: <input type="text" name="description" required><br>
正则表达式: <input type="text" name="pattern" required><br>
动作: <select name="action">
<option value="block">阻止</option>
<option value="log">记录</option>
</select><br>
<button type="submit">添加规则</button>
</form>
<h3>规则列表</h3>
<div id="rules-list">加载中...</div>
<script>
// 使用AJAX获取规则列表
function loadRules() {
fetch('?action=list')
.then(response => response.text())
.then(data => {
const rules = JSON.parse(data);
let html = '<table>';
html += '<tr><th>序号</th><th>描述</th><th>正则表达式</th><th>动作</th><th>操作</th></tr>';
rules.forEach((rule, index) => {
html += `<tr>
<td>${index + 1}</td>
<td>${rule.description}</td>
<td>${rule.pattern}</td>
<td>${rule.action}</td>
<td><button onclick="deleteRule(${index})">删除</button></td>
</tr>`;
});
html += '</table>';
document.getElementById('rules-list').innerHTML = html;
})
.catch(error => {
document.getElementById('rules-list').innerHTML = '加载规则失败: ' + error.message;
});
}
// 删除规则
function deleteRule(index) {
if (confirm('确定要删除这条规则吗?')) {
fetch(`?action=delete&index=${index}`)
.then(response => response.text())
.then(message => {
alert(message);
loadRules();
})
.catch(error => {
alert('删除规则失败: ' + error.message);
});
}
}
// 页面加载完成后加载规则
document.addEventListener('DOMContentLoaded', loadRules);
</script>
</div>
</body>
</html>
]])
end
6. 配置Nginx访问控制界面
在Nginx配置中添加对管理界面的访问控制:
# nginx.conf
http {
# ... 其他配置 ...
server {
listen 80;
server_name example.com;
# WAF规则管理界面
location /waf_manager {
# 限制访问IP
allow 192.168.1.0/24; # 允许内部网络访问
deny all; # 拒绝其他所有IP
default_type 'text/html';
content_by_lua_file /path/to/your/waf_manager.lua;
}
# 正常网站访问
location / {
access_by_lua_file /path/to/your/waf.lua;
# ... 其他配置 ...
}
}
}
7. 日志记录与监控
完善WAF的日志记录功能,以便于后续分析和监控:
-- 在waf.lua中添加日志记录函数
local function log_attack(rule, request_info)
local log_file = "/var/log/nginx/waf.log"
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local log_entry = string.format(
"[%s] [WAF] [%s] [%s] [%s] [%s] [%s]\n",
timestamp,
rule.description,
ngx.var.remote_addr,
ngx.var.request_method,
ngx.var.uri,
request_info or ""
)
local file = io.open(log_file, "a")
if file then
file:write(log_entry)
file:close()
end
end
8. 测试与优化
完成WAF的基本实现后,需要进行充分的测试:
- 使用OWASP ZAP等工具进行漏洞扫描,验证WAF的防护效果
- 在测试环境中模拟各种攻击,检查WAF是否能正确拦截
- 分析日志,优化规则,减少误报率
通过以上步骤,可以实现一个基于Nginx和Lua的WAF系统,并通过可视化界面进行配置管理。