实战教程 ---- Nginx结合Lua实现WAF拦截并可视化配置教程框架

发布于:2025-08-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

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的基本实现后,需要进行充分的测试:

  1. 使用OWASP ZAP等工具进行漏洞扫描,验证WAF的防护效果
  2. 在测试环境中模拟各种攻击,检查WAF是否能正确拦截
  3. 分析日志,优化规则,减少误报率

通过以上步骤,可以实现一个基于Nginx和Lua的WAF系统,并通过可视化界面进行配置管理。


网站公告

今日签到

点亮在社区的每一天
去签到