防火墙挡不住应用层的攻击——攻击者访问的是你开放的 80/443 端口,请求看起来是"正常"的 HTTP 请求。
这篇讲 Web 最常见的几类漏洞是怎么产生的、怎么防。不需要你是安全专家,但作为站长要知道风险在哪,能看懂扫描报告,能配合开发或自己加固。
建站请选稳定可靠的云服务器。财富云计算 提供独立 IP 机型(适合对外网站)与 NAT 型(无独立公网 IP,价格比独立 IP 机型省一半以上,适合内网服务、游戏服务端)。
一、Web 安全的本质
一句话:永远不要相信用户的输入。
所有来自客户端的数据——URL 参数、表单、Cookie、HTTP 头、上传的文件——都可能被篡改。攻击者不会按你设计的用法来用你的网站。
TOP 10(OWASP)里最常见的:
- 注入(SQL、命令、LDAP)
- 失效的身份认证
- 敏感信息泄露
- XML 外部实体(XXE)
- 失效的访问控制(越权)
- 安全配置错误
- 跨站脚本(XSS)
- 不安全的反序列化
- 使用含已知漏洞的组件
- 日志与监控不足
下面挑最常见的几个讲。
二、SQL 注入( Injection)
原理
程序把用户输入直接拼进 SQL 语句:
// 危险写法
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";攻击者传 id=1 OR 1=1:
SELECT * FROM users WHERE id = 1 OR 1=1 -- 返回所有用户传 id=1; DROP TABLE users;-- 就更糟了。
危害
- 拖库(拿到全部用户数据)
- 绕过登录(
' OR '1'='1) - 删库
- 读写文件(某些数据库支持)
- 拿服务器权限(结合
INTO OUTFILE写 webshell)
怎么防
① 参数化查询(预编译语句)—— 唯一推荐方案
// PDO 预处理
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();# Python
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))// Java PreparedStatement
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setInt(1, userId);② 如果必须用拼接(极少见),严格转义 + 白名单
// 数字型强制转类型
$id = intval($_GET['id']);③ 最小权限
数据库账号不要用 root:
CREATE USER 'web'@'localhost' IDENTIFIED BY 'xxx';
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'web'@'localhost';
-- 不给 DROP、FILE、PROCESS 权限④ 错误信息不要暴露给前端
// 生产环境关闭错误显示
ini_set('display_errors', 0);自检
# 用 sqlmap 测试(仅测试自己的站点!)
sqlmap -u "http://example.com/page?id=1" --batch⚠️ 只对自己有授权的站点做测试,扫描他人网站是违法行为。
三、XSS(跨站脚本)
原理
用户输入被当作 HTML/JS 输出到页面上:
<!-- 危险 -->
<div><?php echo $_GET['name']; ?></div>攻击者传:
?name=<script>alert(document.cookie)</script>三种类型
| 类型 | 说明 |
|---|---|
| 反射型 | URL 参数直接输出,需要诱导用户点链接 |
| 存储型 | 存进数据库(如评论、昵称),所有访问者中招(危害大) |
| DOM 型 | 前端 JS 操作 DOM 时引入 |
危害
- 盗取 Cookie(劫持登录态)
- 钓鱼(伪造登录框)
- 键盘记录
- 篡改页面内容
- 配合 CSRF 执行操作
怎么防
① 输出转义(最核心)
echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8');// 前端用 textContent 而不是 innerHTML
element.textContent = userInput;② 富文本内容用白名单过滤
用户要发 HTML(如文章)时,不能用简单转义,要用 HTML Purifier 之类的库:
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$clean = $purifier->purify($dirtyHtml);③ 设置 HttpOnly Cookie
即使有 XSS,也拿不到 Cookie:
setcookie('session', $value, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);④ CSP(内容安全策略)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none';" always;严格版(禁止内联脚本,需要改代码):
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;" always;四、文件上传漏洞
原理
上传功能没限制好,攻击者上传 PHP 文件并执行:
// 危险:直接用原始文件名,不校验
move_uploaded_file($_FILES['f']['tmp_name'], '/uploads/' . $_FILES['f']['name']);
// 上传 shell.php → 访问 /uploads/shell.php → 拿到服务器权限常见绕过手法
- 改 Content-Type(前端校验形同虚设)
- 双后缀
shell.php.jpg(某些解析漏洞) .htaccess上传(修改解析规则)%00截断(老版本 PHP)- 大小写 / 空格绕过黑名单
怎么防
① 白名单后缀(不要用黑名单)
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf'];
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) die('不允许的文件类型');② 重命名文件(不要用原始文件名)
$newName = md5(uniqid()) . '.' . $ext;③ 检查真实类型(不能只看扩展名)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmpName);
if (!in_array($mime, ['image/jpeg', 'image/png', 'image/gif'])) die('类型错误');④ 上传目录禁止执行 PHP(最关键的一层)
location ^~ /uploads/ {
location ~ \.(php|php5|phtml)$ {
deny all;
}
}⑤ 上传目录不放解析权限,且独立域名/对象存储
最彻底的方案:图片传到对象存储(OSS/COS),服务器上根本不存用户上传的文件。
⑥ 限制大小
if ($_FILES['f']['size'] > 5 * 1024 * 1024) die('文件过大');client_max_body_size 10m;五、越权(失效的访问控制)
水平越权
改个 ID 就能看别人的数据:
GET /api/order/12345 → 改成 12346 就看到别人的订单防:每次查询都要带上当前用户身份:
SELECT * FROM orders WHERE id = ? AND user_id = ?垂直越权
普通用户访问了管理员接口:
GET /admin/users → 没做权限校验防:每个接口都要做权限校验,不能只靠"前端不显示按钮"。
if (!$user->isAdmin()) {
http_response_code(403);
exit;
}未授权访问(组件层面)
| 组件 | 默认端口 | 风险 |
|---|---|---|
| Redis | 6379 | 无密码可直接写入,拿服务器权限 |
| MongoDB | 27017 | 同上 |
| Elasticsearch | 9200 | 数据全泄露 |
| Memcached | 11211 | 可被利用做反射放大攻击 |
| Docker API | 2375 | 直接控制容器,等同于服务器权限 |
| MySQL | 3306 | 拖库、写文件 |
防:
# 只监听本地
# redis.conf
bind 127.0.0.1
requirepass 强密码
# 防火墙只放行内网
ufw allow from 10.0.0.0/8 to any port 6379可以用这个命令自查:
ss -tulnp | grep -E "6379|27017|9200|11211|2375|3306"
# 如果看到 0.0.0.0:6379 就要警惕六、CSRF(跨站请求伪造)
原理
用户登录了 A 站,访问了恶意 B 站,B 站自动发请求到 A 站(浏览器会带上 A 站的 Cookie):
<!-- 恶意页面 -->
<img src="https://bank.com/transfer?to=hacker&amount=10000">怎么防
① CSRF Token
// 表单里放 token
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
// 验证
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) die('非法请求');② SameSite Cookie
setcookie('session', $value, ['samesite' => 'Lax']);# 或 Nginx 统一加
proxy_cookie_flags ~ samesite=lax secure;③ 验证 Referer/Origin(辅助手段)
④ 重要操作二次确认(输入密码、短信验证码)
七、安全配置错误(最常见也最容易修)
1)暴露敏感文件
# 检查这些能不能被访问
curl -I https://example.com/.git/config
curl -I https://example.com/.env
curl -I https://example.com/backup.sql
curl -I https://example.com/phpinfo.php
curl -I https://example.com/adminer.php防:
location ~ /\. {
deny all;
access_log off;
}
location ~* \.(sql|bak|swp|old|zip|tar\.gz|log)$ {
deny all;
}2)目录列表被开启
autoindex off; # 确认是 off3)使用默认账号密码
- 数据库 root/root
- 后台 admin/admin
- Redis 空密码
- 面板默认端口 + 默认密码
4)程序/插件版本过旧
# 定期更新
apt update && apt upgrade -y
# 关注 CMS 的安全公告5)开启了调试模式
// 生产环境
define('WP_DEBUG', false);
ini_set('display_errors', 0);6)HTTP 安全响应头缺失
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'" always;去 securityheaders.com 测一下得分。
八、应用层防护手段
1)WAF(Web 应用防火墙)
| 方案 | 说明 |
|---|---|
| 云 WAF(CDN 自带) | 接入即可,无需改服务器,推荐 |
| ModSecurity + Nginx | 开源 WAF,需自己编译配置 |
| Naxsi | Nginx 模块,轻量 |
ModSecurity 安装(Ubuntu):
apt install libapache2-mod-security2 -y # Apache 版直接可用
# Nginx 版需编译,或用 docker 方案简单起见,小站用云 WAF(很多 CDN 免费版就带基础防护)。
2)Nginx 层限流(防 CC)
http {
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
}
server {
limit_req zone=req burst=20 nodelay;
limit_conn conn 20;
# 特定接口更严格
location /api/ {
limit_req zone=req burst=5 nodelay;
}
location = /login.php {
limit_req zone=req burst=3 nodelay;
}
}3)后台限制 IP / 二次认证
location ^~ /admin/ {
allow 你的IP;
deny all;
# 记得保留 PHP 解析
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}4)改后台路径
把 /admin、/wp-login.php 改成不常见的路径(Typecho 在 config.inc.php 里改,WordPress 用插件)。
5)删除不必要的东西
rm -f /var/www/html/phpinfo.php
rm -f /var/www/html/install.php # 安装完就删
rm -rf /var/www/html/adminer.php
rm -rf /var/www/html/test/九、自检清单
快速自查(5 分钟)
# 1. 检查敏感文件是否可访问
for f in .git/config .env backup.sql phpinfo.php adminer.php; do
echo -n "$f: "; curl -s -o /dev/null -w "%{http_code}\n" https://example.com/$f
done
# 2. 检查危险端口是否对外开放
ss -tulnp | grep -E "0.0.0.0:(6379|27017|9200|11211|2375|3306)"
# 3. 检查目录列表
curl -s https://example.com/uploads/ | grep -i "index of"
# 4. 检查 HTTP 头
curl -I https://example.com
# 5. 检查 SSL
# https://www.ssllabs.com/ssltest/
# 6. 找 webshell
grep -rn "eval(\|base64_decode\|system(" /var/www --include="*.php" | head -20
# 7. 看访问日志里的攻击尝试
grep -E "union.*select|\.\./|<script|eval\(" /var/log/nginx/access.log | head -20定期做的事
- [ ] 每月检查程序/插件更新
- [ ] 每季度做一次漏洞扫描(可用 AWVS、Nessus、或在线工具)
- [ ] 每年做一次渗透测试(重要业务)
- [ ] 开启日志审计 + 异常告警
- [ ] 备份 + 恢复演练
十、被攻击时的应急
# 1. 立即看日志
tail -1000 /var/log/nginx/access.log | grep "攻击特征"
# 2. 封 IP
ufw deny from 攻击IP
# 或用 fail2ban
# 3. 如果已被入侵
# → 见《服务器被入侵应急排查与处置》
# 4. 保留证据(日志不要删)Ngi nx 临时封禁大量 IP:
# /etc/nginx/conf.d/blocked.conf
deny 1.2.3.4;
deny 5.6.7.0/24;十一、给不同类型站点的建议
| 站点类型 | 重点防护 |
|---|---|
| 个人博客(Typecho/WordPress) | 及时更新、改后台路径、上传目录禁 PHP、装安全插件 |
| 企业官网 | WAF、定期扫描、后台 IP 限制 |
| 电商/交易 | 全站 HTTPS、支付接口签名、风控、专业渗透测试 |
| API 服务 | 鉴权(JWT/OAuth)、限流、参数校验、日志审计 |
| 游戏服务端 | RCON 强密码、不暴露管理端口、DDoS 防护 |
| 论坛/UGC | 内容过滤、XSS 防护、反垃圾 |
网站安全是持续的事,不是配一次就完事。选一台稳定的云服务器是第一步——财富云计算 提供独立 IP 机型(适合对外网站)与 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上,适合内网服务、游戏服务端,不直接暴露在公网上,受攻击面大幅降低)。建议配合快照备份,出问题能快速回滚。
相关阅读:
- 《服务器被入侵应急排查与处置》
- 《Linux 防火墙配置:ufw / firewalld / iptables》
- 《fail2ban 防暴力破解实战配置》
- 《服务器备份与快照策略》
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]# 用 last/permanent 分清语义,配 error_log info 看跳转链完整 Web 防护见网站常见漏洞与基础防护和WAF 规则实战。七、速查表需求写法伪静态try_files $uri $uri/ /index.php?$query_string;HTTP→HTTPSreturn 301 https://$host$request_uri;www 跳主域return 301 h[...]
2026-10-15 回复
[...]}部署后第一件事:自己访问 http://你的域名/.git/config,返回 404 才算安全,返回文件内容就是裸奔。完整加固见网站常见漏洞与基础防护。2.5 私密仓库 + 最小权限内部项目一律 Private,别用 Public「顺便分享」协作者按需邀请,离职立刻移除开启仓库的 Secret Scanning(GitHub 自带,泄露密钥自动告警)云平台 AccessKey 用子账号,按服务[...]
2026-10-15 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复
[...])三条铁律:永远参数化,ORM(Sequelize/TypeORM/Prisma)同理绝不拼接 SQL,包括 ORDER BY 字段名(白名单校验)错误信息别返回给前端——err.message 里的表结构是给攻击者的礼物,统一返回"服务器错误"配套防护:WAF 规则过滤常见注入特征,见WAF 规则实战和网站常见漏洞与基础防护。五、Redis:会话和缓存的标配小程序登录态放 Redis 是标准姿势[...]
2026-10-18 回复