网站安全加固完全指南: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/