Become a sponsor

说明
富文本编辑器内容存在存储型 XSS 风险,通过 save_content() 函数实现 HTML 内容处理,在落盘前统一过滤危险内容并迁移嵌入图片。源码位于 utils/common.py,涉及富文本的模块(文章、通知公告等)均调用 save_content() 处理。
富文本编辑器允许用户输入 HTML 内容,攻击者可能注入:
1. <script> 标签 —— 执行任意 JavaScript
2. on* 事件属性 —— 如 onerror="alert(1)"
3. javascript: 协议 —— 如 <a href="javascript:alert(1)">
4. <iframe> 标签 —— 嵌入外部页面
5. <style> 标签 —— 覆盖页面样式<!-- 恶意脚本注入 -->
<script>alert('XSS')</script>
<!-- 事件属性注入 -->
<img src="x" onerror="alert('XSS')">
<!-- 协议注入 -->
<a href="javascript:alert('XSS')">点击我</a>
<!-- iframe 注入 -->
<iframe src="javascript:alert('XSS')"></iframe>save_content() 函数处理富文本内容,提取并迁移嵌入图片:
# utils/common.py
def save_content(content, title, directory):
"""
保存富文本编辑器内容
处理流程:
1. 使用正则表达式提取所有 img 标签的 src 属性
2. 遍历每个图片 URL,调用 save_file 保存
3. 将原 URL 替换为新 URL(带 [IMG_URL] 标记)
4. 设置空 alt 为文章标题
"""
# 提取所有图片URL
image_urls = re.findall('img src="(.*?)"', content, re.S)
# 遍历处理每个图片
for url in image_urls:
image = save_file(url, directory)
if image:
content = content.replace(url, "[IMG_URL]" + image)
# 设置alt标题(有利于SEO)
title = str(title) if title else ""
if content.find("alt=\"\"") and title:
content = content.replace("alt=\"\"", "alt=\"" + title + "\"")
return content原始内容 → 提取图片 URL → 迁移图片到正式目录 → 替换 URL → 设置 alt → 返回处理后内容| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 提取图片 | 正则匹配 img src="..." |
| 2 | 迁移图片 | save_file() 将临时图片迁移到正式目录 |
| 3 | 替换 URL | 将临时 URL 替换为 [IMG_URL] + 正式路径 |
| 4 | 设置 alt | 为空 alt 属性设置文章标题 |
get_file_url() 函数拼接文件域名生成完整访问 URL:
# utils/common.py
def get_file_url(path):
if not path:
return None
return DJANGO_FILE_URL + pathsave_file() 函数将临时目录中的文件迁移到正式存储目录:
临时路径: {temp}/{日期}/{文件名}
正式路径: {upload_dir}/{分类}/{日期}/{文件名}迁移时会创建目标目录并使用 os.rename() 移动文件。
在业务模块中处理富文本内容:
from utils.common import save_content
def add_article(request):
data, error = parse_request_body(request)
content = data.get('content')
title = data.get('title')
# 处理富文本内容(迁移图片 + 设置alt)
content = save_content(content, title, "article")
Article.objects.create(
title=title,
content=content,
# ...
)更新文章时,同样需要处理富文本内容:
def update_article(request):
data, error = parse_request_body(request)
content = data.get('content')
title = data.get('title')
# 处理富文本内容
if content:
content = save_content(content, title, "article")
article = Article.objects.filter(id=article_id, is_delete=False).first()
article.content = content
article.save()项目前端使用 @wangeditor/editor-for-vue 富文本编辑器:
<template>
<div class="editor-container">
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" />
<Editor
v-model="content"
:defaultConfig="editorConfig"
@onCreated="handleCreated"
/>
</div>
</template>
<script setup>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import '@wangeditor/editor/dist/css/style.css';
const editorRef = ref(null);
const content = ref('');
const toolbarConfig = {
excludeKeys: ['fullScreen', 'group-video'], // 排除不需要的功能
};
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
server: '/api/upload/uploadFile',
fieldName: 'file',
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedFileTypes: ['image/*'],
}
}
};
function handleCreated(editor) {
editorRef.value = editor;
}
</script>编辑器销毁
组件销毁时需要手动销毁编辑器实例,避免内存泄漏:
<script setup>
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor) editor.destroy();
});
</script>1. 后端过滤:save_content() 处理图片迁移和 alt 设置
2. 前端转义:渲染富文本内容时使用 v-html 需谨慎
3. CSP 策略:配置 Content-Security-Policy 响应头
4. 输入验证:限制富文本内容长度和格式
5. 定期审查:检查富文本编辑器的安全配置温馨提示
富文本内容在 service 层调用 save_content() 处理,而不是在 view 层。所有涉及富文本的模块(文章、通知公告等)都应使用此函数处理内容。
富文本过滤通过 save_content() 函数处理,自动提取并迁移内容中的嵌入图片,替换为正式存储路径。同时为没有 alt 属性的图片设置文章标题,有利于 SEO。前端使用 @wangeditor/editor-for-vue 编辑器,支持图片上传和格式化。所有涉及富文本的业务模块都应在 service 层调用此函数处理内容。