本文目录导读:

- 核心原则:输出编码(最根本的防护)
- 输入侧过滤(辅助手段,不是主要手段)
- 使用安全的富文本编辑器(如果允许 HTML)
- 设置 HTTP 安全响应头(浏览器端最后一关)
- 防止存储型 XSS 的纵深防御
- 一个安全的留言板应该做到
- 常见的错误做法(不要这样做)
留言板是跨站脚本攻击(XSS)的高发区域,因为用户输入的内容会直接或间接地展示给其他用户,防护 XSS 需要从输入、输出和上下文三个维度进行系统性防御。
以下是针对留言板 XSS 防护的核心策略,按推荐等级排列:
核心原则:输出编码(最根本的防护)
不要把用户输入的内容直接当成 HTML 代码解析,根据内容出现的位置,进行不同的转义。
- 位置:HTML 标签内容(如
<div>{用户留言}</div>)- 转义字符:
&->&;<-><;>->>; ->"; ->'
- 转义字符:
- 位置:HTML 属性(如
<input value=" {用户留言} ">)- 转义: 除了上述字符,还需要对空格、制表符、引号等进行严格编码。
- 位置:JavaScript 代码(如
<script>var msg = '{用户留言}'</script>)- 转义: 使用
\n、、 等反斜杠转义,或使用JSON.stringify进行安全转换。
- 转义: 使用
- 位置:URL(如
<a href=" {用户留言} ">)- 转义: 使用 URL 编码(
encodeURIComponent),并严格验证协议为http://或https://,禁止javascript:、data:等协议。
- 转义: 使用 URL 编码(
关键原则: 绝对不要相信用户的输入,即使输入了 <script>alert(1)</script>,经过 HTML 编码后,它只会显示为普通文本,而不会执行。
输入侧过滤(辅助手段,不是主要手段)
虽然输出编码是根本,但输入过滤可以作为第一道防线,减少攻击载荷。
- 白名单策略(推荐): 如果你只需要用户输入纯文本(如留言内容),直接剥离所有 HTML 标签,只允许特定的、安全的 Markdown 或 BBCode 语法。
- 黑名单策略(不推荐): 过滤
<script>、onerror、javascript:等关键词。攻击者总是能找到绕过方式(如大小写、编码、双写等)。 - 长度限制: 设置合理的字符/字节限制(如 500 字),可以增加攻击复杂度。
使用安全的富文本编辑器(如果允许 HTML)
如果你必须允许用户输入富文本(如加粗、斜体、插入图片),情况会更复杂,因为你需要允许部分 HTML。
- 最佳实践:使用前端库进行净化(Sanitization)
- DOMPurify: 目前最主流的客户端 HTML 净化库,它会在浏览器中解析用户输入的 HTML,然后删除所有不安全的标签和属性(如
<script>、onclick、<iframe>、<style>)。 - 如何使用:
// 用户提交的内容 var dirty = '<img src="x" onerror="alert(1)"><b>你好</b>'; // 净化后的内容 var clean = DOMPurify.sanitize(dirty); // 输出 clean: '<b>你好</b>'(img 被移除了)
- DOMPurify: 目前最主流的客户端 HTML 净化库,它会在浏览器中解析用户输入的 HTML,然后删除所有不安全的标签和属性(如
- 后端也需做一次净化: 永远不要假设前端提交的数据是安全的,在后端(如 Node.js 的
sanitize-html,Python 的bleach,PHP 的HTMLPurifier)再次进行净化。
设置 HTTP 安全响应头(浏览器端最后一关)
这些头部可以告诉浏览器如何解释页面内容,是一种深度防御。
-
Content-Security-Policy (CSP): 最强大的防护。
- 策略示例:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; - 作用:告知浏览器只允许加载同源的脚本,或者仅信任特定 CDN 的脚本,即使攻击者注入了
<script src="恶意链接">,浏览器也会拒绝加载。 - 注意: 如果留言板内使用了内联脚本(如
<script>alert(1)</script>),CSP 需要设置'unsafe-inline',但这会降低安全性。
- 策略示例:
-
X-Content-Type-Options: nosniff:
防止浏览器将 HTML 文档误判为其他类型(如从图片中解析出 JS 并执行)。
-
X-Frame-Options: DENY(或 SAMEORIGIN):
如果留言板页面可能被嵌入在 iframe 中(如用于钓鱼),可以设置请求禁止被嵌入。
防止存储型 XSS 的纵深防御
- Cookie 安全:
- 设置
HttpOnly标志:JavaScript 无法通过document.cookie读取到该 Cookie,从而防止攻击者窃取 Session ID。 - 设置
SameSite=Lax或Strict:限制跨站请求携带 Cookie。
- 设置
- CSRF Token: 如果攻击者想利用留言板提交恶意脚本,CSRF Token 可以防止跨站请求伪造。
- 定期审计: 定期检查留言内容,是否有人发布了可疑的 HTML 或 JavaScript 代码。
一个安全的留言板应该做到
- 输出编码是必须的: 即使是简单的
<和>也要编码。 - 如果允许富文本,必须使用
DOMPurify或类似库进行净化。 - 后端必须覆写一次净化逻辑(永远不信任前端)。
- 设置 CSP 头,限制脚本来源(这是最后的防线)。
- 浏览器级别防御:
HttpOnly+X-Content-Type-Options。
常见的错误做法(不要这样做)
- ❌ 直接使用
innerHTML插入用户内容:element.innerHTML = userContent; - ❌ 使用
eval()或new Function()执行用户输入。 - ❌ 手动拼接字符串且仅替换关键词: 比如只替换
<script>(攻击者可以用<img src=x onerror=alert(1)>绕过)。 - ❌ 对用户输入进行单次过滤后就以为万事大吉。(一定要做输出转义)
如果你使用的是 Vue、React 等现代框架,它们默认会对模板中的变量进行转义(如 { { userInput } }),这可以防御大部分注入,但如果你使用了 v-html 或 dangerouslySetInnerHTML,则需要自己负责安全。