网站安全加固完全指南:XSS、CSRF、SQL 注入防护实践
网站安全不是可选项,而是建站的基础要求。据 Verizon 数据泄露报告,43% 的网络攻击针对小型网站——原因很简单:小站通常没有专职安全团队,暴露面却不比大站少。对绝大多数站长来说,真正的风险不是国家级攻击者,而是自动化扫描脚本和利用常见漏洞的批量攻击。把下面这几类防护做扎实,就能挡掉 90% 以上的日常威胁。
OWASP Top 10 关键威胁
OWASP(开放 Web 应用安全项目)每三年更新一次 Web 应用十大安全风险。最新的 Top 10 里,注入(Injection)、失效的访问控制(Broken Access Control)和 XSS 长期占据前列。本文聚焦普通建站团队最容易遇到、也最容易防住的三种:XSS、CSRF 与 SQL 注入。
一、XSS(跨站脚本攻击)
XSS 攻击者将恶意脚本注入网页,在用户浏览器中执行。最常见的是存储型 XSS:用户在评论、昵称、富文本里输入一段 <script>,服务端没转义就存进数据库,其他用户打开页面时脚本就在他们的浏览器里跑起来,可能偷走 Cookie、劫持会话或植入钓鱼弹窗。
防护方案
1. 输出编码
// 前端编码函数
function escapeHtml(str) {
const div = document.createElement("div");
div.textContent = str;
return div.innerHTML;
}
// React 默认对 JSX 变量进行转义
// Vue 使用 {{ }} 会转义,v-html 不会
记住两条原则:输入校验、输出编码。凡是用户可控的内容,渲染前一律转义;确实需要富文本的场景,用白名单过滤(允许的标签和属性)而不是黑名单。
2. Content Security Policy(CSP)
# 只允许加载同源资源
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
CSP 是 XSS 的"最后防线":即使脚本被注入,只要 CSP 拒绝非白名单来源,浏览器就不会执行。上线初期建议先开 Content-Security-Policy-Report-Only 观察违规报告,再切到强制执行。
3. 设置 HttpOnly Cookie
// 设置 Cookie 时标记 HttpOnly
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax;
HttpOnly 让 JavaScript 读不到 Cookie,即使 XSS 发生,攻击者也拿不到会话。
二、CSRF(跨站请求伪造)
CSRF 攻击者诱导用户点击链接,在已登录状态下执行非自愿操作。典型场景:你在论坛发帖,帖子里嵌了 <img src="https://bank.example.com/transfer?to=hacker&amount=1000">,浏览器会自动带上你的登录 Cookie 发出请求。对 GET 请求执行"转账"这类操作的老系统尤其危险。
防护方案
1. CSRF Token
<form method="POST" action="/transfer">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="text" name="amount">
<button type="submit">提交</button>
</form>
服务端为每个会话生成随机 Token,表单提交时校验。现代框架(Laravel、Django、Rails、Spring)都内置了 CSRF 保护,默认开启即可,关键是别为了省事关掉它。
2. SameSite Cookie 属性
Set-Cookie: session=abc123; SameSite=Strict; Secure;
SameSite=Strict 可防止所有跨站 Cookie 发送,SameSite=Lax 允许部分安全请求。对绝大多数网站,Lax 是体验和安全之间的平衡点,Strict 更适合后台管理系统。
3. 验证 Referer/Origin 头
// 服务端验证
function isValidRequest(req) {
const origin = req.headers.origin;
const allowedOrigins = ['https://example.com'];
return allowedOrigins.includes(origin);
}
三、SQL 注入
SQL 注入攻击者通过输入恶意 SQL 片段操纵数据库查询。最经典的例子:登录框输入 ' OR '1'='1,如果服务端用字符串拼接查询,攻击者可能直接绕过认证或拖走整张表。
防护方案
1. 参数化查询(最重要)
// ❌ 错误:字符串拼接
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ 正确:参数化查询 (Node.js + pg)
const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
// ✅ 正确:参数化查询 (PHP + PDO)
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $userId]);
2. ORM 框架
使用 Prisma、TypeORM、Eloquent、SQLAlchemy 等 ORM 可以自动避免 SQL 注入。
// Prisma 安全查询
const user = await prisma.user.findUnique({
where: { id: userId }
});
3. 最小权限原则
数据库用户只应拥有其功能所需的最小权限。不要使用 root 连接数据库。
-- 只给应用用户 SELECT, INSERT, UPDATE, DELETE 权限
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'localhost';
四、更多安全实践
4.1 密码安全
// 使用 bcrypt 哈希密码
const bcrypt = require('bcrypt');
const salt = await bcrypt.genSalt(12);
const hash = await bcrypt.hash(password, salt);
密码绝不明文存储,也不要用 MD5/SHA1(可被彩虹表破解),用 bcrypt、argon2 这类带盐的慢哈希。
4.2 HTTPS 强制
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
同时开启 HSTS 头(Strict-Transport-Security),让浏览器强制走 HTTPS。
4.3 文件上传安全
- 限制文件类型(白名单方式)
- 限制文件大小
- 上传到独立域名(防止 XSS)
- 对上传文件进行病毒扫描
4.4 一个真实案例
某电商后台曾因上传接口只校验了 MIME 类型,被攻击者上传了伪装成图片的 PHP 脚本并在服务器上执行,整站被植入后门。修复方式是:按扩展名白名单过滤、文件重命名(去掉用户可控文件名)、存到 Web 根目录之外并用单独域名通过受控脚本访问。这类漏洞往往不是"新技术",而是"基础步骤没做全"。
安全检查清单
- 所有用户输入都经过验证和转义
- 使用参数化查询或 ORM
- 启用 HTTPS 并配置 HSTS
- 设置安全响应头(CSP, X-Frame-Options 等)
- CSRF Token 或 SameSite Cookie 已配置
- 密码使用 bcrypt/argon2 哈希
- 文件和目录权限正确
- 软件和依赖定期更新
- 日志记录和监控已启用
- 数据备份策略已制定
16IDC 观察
网站安全防护的核心不是单一措施,而是多层防御(Defense in Depth)。即使配置了 WAF 和 CDN,应用层的安全编码仍然是不可替代的基础防线——WAF 能拦常见的签名,但业务逻辑漏洞只有代码层能修。新服务器上线前的初始化加固也很关键,可参考服务器初始化安全;HTTPS 与证书相关见SSL/TLS 部署指南。
参考:OWASP Top 10 https://owasp.org/www-project-top-ten/;Verizon 数据泄露报告 https://www.verizon.com/business/resources/reports/dbir/