表单安全
表单是用户与网站交互的主要入口,也是安全攻击的重点目标。CSRF(跨站请求伪造)、输入验证不足、CAPTCHA 绕过等问题可能导致数据泄露和恶意操作。本节将介绍 CSRF 防护策略、客户端与服务端输入验证、CAPTCHA 实现方法。
前置知识
阅读本节前,建议先了解:iframe 安全
CSRF 防护
什么是 CSRF
CSRF(Cross-Site Request Forgery)利用用户已认证的会话,在用户不知情的情况下发送恶意请求。
用户已登录银行网站 → 访问恶意网站 → 恶意网站发送银行转账请求 → 银行执行转账CSRF Token
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CSRF 防护示例</title>
</head>
<body>
<form action="/transfer" method="POST">
<!-- 服务器生成的 CSRF Token -->
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6" />
<label for="to-account">转账到</label>
<input type="text" id="to-account" name="to_account" />
<label for="amount">金额</label>
<input type="number" id="amount" name="amount" min="0.01" step="0.01" />
<button type="submit">确认转账</button>
</form>
</body>
</html>SameSite Cookie
# 服务器设置 SameSite Cookie
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
# SameSite 值
# Strict: 完全禁止跨站 Cookie(最安全)
# Lax: 允许安全的跨站 GET 请求
# None: 允许所有跨站请求(不安全)双重 Cookie 验证
html
<!-- 服务器在 Cookie 和表单中都设置 Token -->
<form action="/transfer" method="POST">
<!-- 从 Cookie 中读取的 Token -->
<input type="hidden" name="csrf_token" value="..." />
<!-- 服务器对比 Cookie 和表单中的 Token 是否一致 -->
<button type="submit">提交</button>
</form>输入验证
客户端验证
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表单验证示例</title>
</head>
<body>
<form id="register-form" action="/register" method="POST">
<div>
<label for="username">用户名</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="20"
pattern="[a-zA-Z0-9_]+"
aria-describedby="username-hint"
/>
<span id="username-hint">3-20 个字符,仅支持字母、数字和下划线</span>
</div>
<div>
<label for="email">邮箱</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-hint"
/>
<span id="email-hint">请输入有效的邮箱地址</span>
</div>
<div>
<label for="age">年龄</label>
<input
type="number"
id="age"
name="age"
min="18"
max="120"
required
/>
</div>
<div>
<label for="website">个人网站(可选)</label>
<input type="url" id="website" name="website" />
</div>
<button type="submit">注册</button>
</form>
<script>
// 客户端验证(JavaScript)
const form = document.getElementById('register-form');
form.addEventListener('submit', function(e) {
// 自定义验证
const username = document.getElementById('username').value;
if (username.includes('admin') || username.includes('root')) {
e.preventDefault();
alert('用户名不能包含保留词');
return;
}
// 如果有自定义验证,使用 setCustomValidity
const email = document.getElementById('email');
if (email.value.endsWith('.test')) {
email.setCustomValidity('不支持 .test 域名的邮箱');
e.preventDefault();
} else {
email.setCustomValidity('');
}
});
</script>
</body>
</html>服务端验证(必须)
# 服务端必须验证所有输入
# 1. 类型验证:确保输入是预期的类型
# 2. 长度验证:限制输入长度
# 3. 格式验证:正则匹配
# 4. 范围验证:确保值在允许范围内
# 5. 白名单验证:仅允许已知的合法值HTML5 内置验证属性
| 属性 | 说明 |
|---|---|
required | 必填字段 |
minlength / maxlength | 文本长度限制 |
min / max | 数值范围限制 |
pattern | 正则表达式验证 |
type="email" | 邮箱格式验证 |
type="url" | URL 格式验证 |
type="number" | 仅允许数字 |
CAPTCHA
html
<!-- reCAPTCHA v2 -->
<div class="g-recaptcha" data-sitekey="your-site-key"></div>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<!-- reCAPTCHA v3(无用户交互) -->
<script src="https://www.google.com/recaptcha/api.js?render=your-site-key"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('your-site-key', { action: 'submit' }).then(function(token) {
// 将 token 添加到表单
document.getElementById('recaptcha-token').value = token;
});
});
</script>
<input type="hidden" id="recaptcha-token" name="recaptcha_token" />
<!-- Turnstile(Cloudflare 替代方案) -->
<div class="cf-turnstile" data-sitekey="your-site-key" data-callback="javascriptCallback"></div>HTTP 方法安全
html
<!-- 敏感操作使用 POST 而非 GET -->
<!-- 错误:GET 请求可能导致 CSRF 攻击更容易 -->
<a href="/delete-account">删除账户</a>
<!-- 正确:使用 POST + CSRF Token -->
<form action="/delete-account" method="POST">
<input type="hidden" name="csrf_token" value="..." />
<button type="submit" onclick="return confirm('确定要删除账户吗?')">删除账户</button>
</form>注意事项
- 永远不要只依赖客户端验证:服务端必须验证所有输入
- CSRF Token 每次请求都应更新:防止 Token 重放
- 敏感操作需要二次确认:转账、删除等操作需要确认
- 限制请求频率:防止暴力攻击
- 记录所有敏感操作:安全审计需要日志
最佳实践
- 所有表单添加 CSRF Token
- 设置 SameSite=Strict Cookie
- 客户端 + 服务端双重验证
- 使用 HTML5 内置验证属性
- 敏感操作使用 POST 方法
- 重要操作添加 CAPTCHA
- 限制请求频率防止暴力攻击
- 记录安全审计日志
下一节
继续学习:子资源完整性(SRI)