From 284b714c1e669d3a45611ee738dcd7cbe84a8d92 Mon Sep 17 00:00:00 2001
From: ZhangZhiYi <1667783625@qq.com>
Date: Thu, 13 Aug 2026 11:42:51 +0800
Subject: [PATCH] =?UTF-8?q?fix(=E6=BC=8F=E6=B4=9E):=20=E6=96=B0=E9=97=BB?=
=?UTF-8?q?=E3=80=81=E9=80=9A=E7=9F=A5=E5=8A=A0xss=E8=BF=87=E6=BB=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
springboot/fastbee-common/pom.xml | 12 +++
.../common/utils/html/RichTextSanitizer.java | 63 ++++++++++++++++
.../utils/html/RichTextSanitizerTest.java | 75 +++++++++++++++++++
.../iot/service/impl/NewsServiceImpl.java | 3 +
.../main/resources/mapper/iot/NewsMapper.xml | 4 +-
.../service/impl/SysNoticeServiceImpl.java | 3 +
springboot/pom.xml | 8 ++
7 files changed, 166 insertions(+), 2 deletions(-)
create mode 100644 springboot/fastbee-common/src/main/java/com/fastbee/common/utils/html/RichTextSanitizer.java
create mode 100644 springboot/fastbee-common/src/test/java/com/fastbee/common/utils/html/RichTextSanitizerTest.java
diff --git a/springboot/fastbee-common/pom.xml b/springboot/fastbee-common/pom.xml
index 827eaf96..5ab6f131 100644
--- a/springboot/fastbee-common/pom.xml
+++ b/springboot/fastbee-common/pom.xml
@@ -65,6 +65,12 @@
保留 Quill 编辑器常用的安全格式,删除脚本、事件属性、危险协议和未授权标签。
+ */ +public final class RichTextSanitizer +{ + private static final Pattern IMAGE_DIMENSION_PATTERN = Pattern.compile("[0-9]{1,5}"); + private static final Pattern LINK_TARGET_PATTERN = Pattern.compile("(?i)_(?:blank|self)"); + + /** Quill 输出的安全样式类,禁止通过任意 class 扩大前端样式能力。 */ + private static final Pattern QUIL_CLASS_PATTERN = Pattern.compile( + "(?i)(?:ql-(?:align-(?:center|right|justify)|indent-[1-8]|size-(?:small|large|huge)|" + + "direction-rtl|font-(?:serif|monospace)|syntax))" + + "(?:\\s+ql-(?:align-(?:center|right|justify)|indent-[1-8]|size-(?:small|large|huge)|" + + "direction-rtl|font-(?:serif|monospace)|syntax))*"); + + /** + * 策略对象创建成本较高且本身不可变,所有请求复用同一实例。 + * 不允许 iframe、video、object、embed、svg 等主动内容。 + */ + private static final PolicyFactory POLICY = new HtmlPolicyBuilder() + .allowCommonInlineFormattingElements() + .allowCommonBlockElements() + .allowStyling() + .allowUrlProtocols("http", "https") + .allowElements("a", "img", "br", "pre", "div", "span") + .allowWithoutAttributes("a", "br", "span") + .allowAttributes("href").onElements("a") + .allowAttributes("target").matching(LINK_TARGET_PATTERN).onElements("a") + .allowAttributes("alt", "src").onElements("img") + .allowAttributes("height", "width").matching(IMAGE_DIMENSION_PATTERN).onElements("img") + .allowAttributes("class") + .matching(QUIL_CLASS_PATTERN) + .onElements("p", "div", "pre", "ol", "ul", "li", "span") + .requireRelNofollowOnLinks() + .toFactory(); + + private RichTextSanitizer() + { + } + + /** + * 净化不可信富文本。 + * + * @param html 待净化的富文本 + * @return 可安全嵌入 HTML 正文上下文的富文本 + */ + public static String sanitize(String html) + { + if (html == null || html.isEmpty()) + { + return html; + } + return POLICY.sanitize(html); + } +} diff --git a/springboot/fastbee-common/src/test/java/com/fastbee/common/utils/html/RichTextSanitizerTest.java b/springboot/fastbee-common/src/test/java/com/fastbee/common/utils/html/RichTextSanitizerTest.java new file mode 100644 index 00000000..eac1f9bb --- /dev/null +++ b/springboot/fastbee-common/src/test/java/com/fastbee/common/utils/html/RichTextSanitizerTest.java @@ -0,0 +1,75 @@ +package com.fastbee.common.utils.html; + +import org.junit.Assert; +import org.junit.Test; + +/** + * 富文本白名单净化测试。 + */ +public class RichTextSanitizerTest +{ + @Test + public void shouldRemoveExecutableContent() + { + String input = "正文"
+ + "
正文
"); + Assert.assertEquals(sanitized, RichTextSanitizer.sanitize(sanitized)); + } +} diff --git a/springboot/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/NewsServiceImpl.java b/springboot/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/NewsServiceImpl.java index 448b77ae..6ead4e26 100644 --- a/springboot/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/NewsServiceImpl.java +++ b/springboot/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/NewsServiceImpl.java @@ -3,6 +3,7 @@ package com.fastbee.iot.service.impl; import java.util.ArrayList; import java.util.List; import com.fastbee.common.utils.DateUtils; +import com.fastbee.common.utils.html.RichTextSanitizer; import com.fastbee.iot.mapper.NewsCategoryMapper; import com.fastbee.iot.model.CategoryNews; import com.fastbee.iot.model.IdAndName; @@ -88,6 +89,7 @@ public class NewsServiceImpl implements INewsService @Override public int insertNews(News news) { + news.setContent(RichTextSanitizer.sanitize(news.getContent())); news.setCreateTime(DateUtils.getNowDate()); return newsMapper.insertNews(news); } @@ -101,6 +103,7 @@ public class NewsServiceImpl implements INewsService @Override public int updateNews(News news) { + news.setContent(RichTextSanitizer.sanitize(news.getContent())); news.setUpdateTime(DateUtils.getNowDate()); return newsMapper.updateNews(news); } diff --git a/springboot/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/NewsMapper.xml b/springboot/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/NewsMapper.xml index c4eeb2a1..252cf074 100644 --- a/springboot/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/NewsMapper.xml +++ b/springboot/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/NewsMapper.xml @@ -96,7 +96,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update news