13 10 月 2026

防火墙挡不住应用层的攻击——攻击者访问的是你开放的 80/443 端口,请求看起来是"正常"的 HTTP 请求。

这篇讲 Web 最常见的几类漏洞是怎么产生的、怎么防。不需要你是安全专家,但作为站长要知道风险在哪,能看懂扫描报告,能配合开发或自己加固。

建站请选稳定可靠的云服务器。财富云计算 提供独立 IP 机型(适合对外网站)与 NAT 型(无独立公网 IP,价格比独立 IP 机型省一半以上,适合内网服务、游戏服务端)。

一、Web 安全的本质

一句话:永远不要相信用户的输入。

所有来自客户端的数据——URL 参数、表单、Cookie、HTTP 头、上传的文件——都可能被篡改。攻击者不会按你设计的用法来用你的网站。

TOP 10(OWASP)里最常见的:

  1. 注入(SQL、命令、LDAP)
  2. 失效的身份认证
  3. 敏感信息泄露
  4. XML 外部实体(XXE)
  5. 失效的访问控制(越权)
  6. 安全配置错误
  7. 跨站脚本(XSS)
  8. 不安全的反序列化
  9. 使用含已知漏洞的组件
  10. 日志与监控不足

下面挑最常见的几个讲。


二、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;
}

未授权访问(组件层面)

组件默认端口风险
Redis6379无密码可直接写入,拿服务器权限
MongoDB27017同上
Elasticsearch9200数据全泄露
Memcached11211可被利用做反射放大攻击
Docker API2375直接控制容器,等同于服务器权限
MySQL3306拖库、写文件

防:

# 只监听本地
# 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;      # 确认是 off

3)使用默认账号密码

  • 数据库 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,需自己编译配置
NaxsiNginx 模块,轻量

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 防暴力破解实战配置》
  • 《服务器备份与快照策略》
延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:服务器被入侵应急排查与处置:发现异常后该做什么
  2. 下一篇:Linux 用户与 sudo 权限管理详解:多人共用服务器怎么管才不乱
发表评论 4

取消回复