一个真实记录如何把 Python 项目从IP:3210直接暴露变成 WAF→Nginx→App 三层防护的完整过程。---------------------AI角色扮演网站安全加固实战篇二背景继 AI 角色扮演网站安全加固实战篇一之后我又把这个项目搬到了本地的一台老惠普本上刷了个 Linux Mint 22.3 LTS基于 Ubuntu 24.04 LTS当家庭服务器。这个 Python FastAPI 项目通过 aaPanel 面板部署图省事直接 IP:3210 裸奔访问。但这台机器就挂在家庭 WiFi 下跑着同一个局域网、甚至能摸到这个 IP 的人都可以直接打到应用端口。万一碰上恶意扫描、SQL 注入、暴力破解就凭代码里那点安全中间件分分钟被穿成筛子。这波操作模拟的就是最真实的业务防御场景同时也在模拟真实公网业务的防御。补充下原python项目架构之前忘记提及了技术栈层技术Web 框架FastAPI 0.115服务器uvicorn 0.30数据库SQLite (aiosqlite, WAL 模式)认证JWT (HS256, Cookie, 30天过期)HTTP 客户端aiohttp (调用外部 AI API)模板纯 HTML (templates 目录)静态资源FastAPI StaticFiles项目文件结构aichat/ ├── main.py ← 全部业务逻辑单文件 ├── data.db ← SQLite 数据库自动创建 ├── .env ← 环境变量SECRET_KEY / HOST / PORT ├── requirements.txt ← Python 依赖 ├── templates/ │ ├── chat.html ← 聊天主页面 │ ├── login.html ← 登录/注册页 │ └── settings.html ← 设置页 └── static/ ← CSS / JS 等静态资源这次需求很清楚基于 aaPanel 配置 Nginx 反向代理与 UFW 端口访问控制接入雷池 WAF 实现 HTTP 层防护构建适配局域网 IP端口访问模式的应用安全链路。翻译成人话外面的人只能走正门某个指定端口正门有安检WAF 查恶意请求再由传达室Nginx转交到真正干活的人Python 应用躲在内网。直通的后门端口3210焊死。最终架构局域网用户 → 雷池 WAF (:3211) → Nginx (:8081) → Python App (127.0.0.1:3210) HTTP 层攻击检测 反向代理限流 业务逻辑(外部不可达)每个端口谁负责、外面能不能访问端口服务外部可访问3211雷池 WAF唯一入口✅9443雷池管理面板✅8081Nginx 反向代理❌ 仅 127.0.0.13210Python 应用❌ 仅 127.0.0.1实施过程第1步加固应用自身让 Python 应用只监听本机回环地址放弃对外暴露。.env加一行HOST127.0.0.1main.py末尾启动参数加代理头支持uvicorn.run(app,hostHOST,portPORT,proxy_headersTrue,forwarded_allow_ips127.0.0.1)同时加了个中间件解析X-Forwarded-For经过 WAF/Nginx 后还能拿到真实客户端 IP。第2步aaPanel 配置 Nginx 反向代理在 aaPanel 新建站点端口填8081绕开 aaPanel 保留端口的限制。Nginx 配置upstream aichat_backend{# 应用只监听 127.0.0.1:3210不对外暴露server127.0.0.1:3210fail_timeout10smax_fails3;keepalive32;}# ---------- 限流区域 ----------# 常规请求每秒 30 个对 AI 聊天够用limit_req_zone$binary_remote_addrzoneaichat_limit:10mrate30r/s;# 登录接口每分钟 10 个防暴力破解limit_req_zone$binary_remote_addrzoneaichat_login:10mrate10r/m;# ---------- 服务器块 ----------server{# aaPanel 默认会生成 listen 80此处显式声明供 WAF 转发的端口listen8081;server_name _192.168.124.27;# 接受任意域名/IP 访问局域网场景# 日志aaPanel 通常已有日志配置按需调整access_log /www/wwwlogs/aichat_access.log;error_log /www/wwwlogs/aichat_error.log;# 上传限制聊天一般不需要大文件调小防滥用client_max_body_size 10m;client_body_buffer_size 128k;client_header_buffer_size 4k;large_client_header_buffers48k;# 超时SSE 流式响应需较长超时proxy_read_timeout 300s;proxy_send_timeout 300s;proxy_connect_timeout 10s;keepalive_timeout65;# ---------- 安全头 ----------add_header X-Content-Type-Optionsnosniffalways;add_header X-Frame-OptionsDENYalways;add_header X-XSS-Protection1; modeblockalways;add_header Referrer-Policystrict-origin-when-cross-originalways;# 隐藏 Nginx 版本号server_tokens off;# ---------- 主反向代理 ----------location /{# 限流limit_reqzoneaichat_limitburst50nodelay;proxy_pass http://aichat_backend;# ---- 关键传递客户端真实信息给后端 ----proxy_set_header Host$host;proxy_set_header X-Real-IP$remote_addr;# 追加本机 IP 到 X-Forwarded-ForWAF 已将客户端 IP 放在此头proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto$scheme;proxy_set_header X-Forwarded-Host$host;proxy_set_header X-Forwarded-Port$server_port;# ---- WebSocket / SSE 支持 ----proxy_http_version1.1;proxy_set_header Upgrade$http_upgrade;proxy_set_header Connectionupgrade;# ---- 缓冲流式响应关闭缓冲----proxy_buffering off;proxy_cache off;# ---- 错误处理 ----proxy_next_upstream errortimeoutinvalid_header http_500 http_502 http_503;proxy_next_upstream_tries2;}# ---------- 登录接口限流加强 ----------location /api/login{limit_reqzoneaichat_loginburst5nodelay;proxy_pass http://aichat_backend;proxy_set_header Host$host;proxy_set_header X-Real-IP$remote_addr;proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto$scheme;proxy_http_version1.1;}# ---------- 拒绝敏感文件访问 ----------location ~*\.(db|sqlite|sqlite3|env|pyc|py|git|svn)${deny all;return404;}# ---------- 健康检查 ----------location /health{access_log off;return200OK;add_header Content-Type text/plain;}}第3步安装雷池 WAF# 下载脚本wgethttps://waf-ce.chaitin.cn/release/latest/setup.sh# 如果 wget 报证书错就用这个curl-fsSLkhttps://waf-ce.chaitin.cn/release/latest/setup.sh-osetup.sh# 给执行权限chmodx setup.sh# 用 bash 运行注意直接用 bash 命令sudobashsetup.sh后续根据安装引导选Y即可可先按引导说的暂时关闭防火墙后续再开放对应端口然后打开防火墙也不迟。登录管理面板IP:9443建站点上游填127.0.0.1:8081监听端口3211开启攻击防护和CC防护上图为添加我的AI聊天网站截图上图为cc防护配置截图第4步UFW 防火墙配置sudoufw allow9443/tcp# 雷池管理sudoufw deny3210/tcp# 关闭端口封死应用直连sudoufw deny8081/tcp# 关闭端口封死 Nginx 直连sudoufwenable踩坑记录坑1aaPanel 不让用 8080 端口aaPanel 自己占用了 8080新建站点填不了。换成8081解决。端口号不重要只要整个链路一致就行。坑2配置 Nginx 后页面样式全丢Nginx 配置里我错误加了个/static/拦截块导致能访问网页但是格式全丢变成一大堆文字了。这是典型的过早优化——想着让 Nginx 直接托管静态文件提升性能但路径没搞清楚就先加上了。删掉这个块让静态文件透传给 Python 处理立刻恢复。教训先把主链路调通性能优化放最后。验证# 走正门经过 WAF Nginxcurlhttp://服务器IP:3211/ → ✅ 正常页面# 走后门直接摸应用curlhttp://服务器IP:3210/ → ❌ 超时/拒绝# 摸 Nginxcurlhttp://服务器IP:8081/ → ❌ 超时/拒绝总结整个方案让它实现了每一层只做一件事UFW— 只管端口里面跑的一概不管雷池 WAF— 只管 HTTP 层恶意流量不操心路由Nginx— 只管代理转发和限流不操心安全检测Python 应用— 只管业务逻辑躲在最后面套在一起就是一条完整的防护链。任何一层挂了另外两层还在。