Pre Merge pull request !742 from AprilWind/dev-xss1

This commit is contained in:
AprilWind 2025-08-16 03:31:53 +00:00 committed by Gitee
commit af8f9f385a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
3 changed files with 177 additions and 0 deletions

View File

@ -36,6 +36,7 @@
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.38</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<owasp.html-sanitizer.version>20240325.1</owasp.html-sanitizer.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
@ -288,6 +289,12 @@
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
<version>${owasp.html-sanitizer.version}</version>
</dependency>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>

View File

@ -94,6 +94,11 @@
<artifactId>ip2region</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,165 @@
package org.dromara.common.core.utils;
import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
/**
* HTML/XSS 过滤工具类
* 基于 OWASP Java HTML Sanitizer
*
* @author AprilWind
*/
public class HtmlSanitizerUtil {
/**
* 纯文本策略
* 去除所有 HTML 标签仅保留纯文本内容
* 适用于对内容格式无任何要求只需要纯文本的场景如用户名标题等
*/
public static final PolicyFactory TEXT_ONLY_POLICY = new HtmlPolicyBuilder().toFactory();
/**
* 简单展示文本策略
* 允许部分格式化标签介于纯文本和富文本之间
* 适用于简单文本展示如昵称简短描述等
*/
public static final PolicyFactory SIMPLE_TEXT_POLICY = Sanitizers.FORMATTING;
/**
* 严格无脚本策略
* <p>
* 仅允许非常基础的格式化标签如加粗(b/strong)斜体(i/em)下划线(u)
* 段落(p)换行(br)和列表(ul/ol/li)禁止所有标签属性
* 包括样式(style)事件处理器(onXXX)或其他可能引入 XSS 的内容
* </p>
* <p>
* 适用于对安全要求极高且不需要任何复杂格式的场景
* 如纯文本内容的最小格式化需求
* </p>
*/
public static final PolicyFactory STRICT_NO_SCRIPT_POLICY = new HtmlPolicyBuilder()
.allowElements("b", "strong", "i", "em", "u", "p", "br", "ul", "ol", "li")
// 显式声明允许文本出现在这些标签中增强策略清晰度可选
.allowTextIn("b", "strong", "i", "em", "u", "p", "ul", "ol", "li")
.toFactory();
/**
* 评论区策略
* 允许基础格式化标签和超链接
* 防止图片表格等复杂内容
* 适用于评论区文本既保证安全又支持简单的文本格式和链接
*/
public static final PolicyFactory COMMENT_POLICY = Sanitizers.FORMATTING
.and(Sanitizers.LINKS);
/**
* 文章编辑器策略
* 允许格式化标签超链接图片样式块级元素和表格
* 适用于富文本编辑器内容如文章正文兼顾内容丰富和安全
*/
public static final PolicyFactory ARTICLE_POLICY = Sanitizers.FORMATTING
.and(Sanitizers.LINKS)
.and(Sanitizers.IMAGES)
.and(Sanitizers.STYLES)
.and(Sanitizers.BLOCKS)
.and(Sanitizers.TABLES);
/**
* 富文本邮件内容策略
* 允许基础格式化标签超链接图片表格和样式
* 相较于一般富文本编辑器稍微严格一些主要用于邮件内容的安全过滤
* 防止恶意脚本注入同时保证邮件内容的丰富展示效果
*/
public static final PolicyFactory EMAIL_HTML_POLICY = Sanitizers.FORMATTING
.and(Sanitizers.LINKS)
.and(Sanitizers.IMAGES)
.and(Sanitizers.STYLES)
.and(Sanitizers.TABLES);
/**
* CMS 编辑器策略较宽松允许多种标签和属性
*
* <p>该策略适用于内容管理系统中的富文本编辑器支持多样化的HTML标签和常用属性
* 包括超链接图片表格媒体标签等同时允许安全的样式和部分自定义属性</p>
*
* <ul>
* <li>允许常见的结构化标签如段落(p)标题(h1-h6)列表(ul,ol,li)表格(table等)</li>
* <li>支持超链接(a)的href属性图片(img)的srcalt等属性</li>
* <li>支持iframevideoaudio等媒体标签及其常用属性</li>
* <li>允许表格单元格的合并属性colspanrowspan</li>
* <li>通过allowStyling方法允许内联样式并进行安全过滤</li>
* <li>限制URL协议为httphttps和mailto避免javascript等危险协议</li>
* <li>支持部分常用的自定义数据属性data-toggledata-targetdata-id以便前端交互</li>
* <li>支持无障碍相关的aria-*属性和role属性提升辅助设备兼容性</li>
* <li>允许iframe的特定属性allowfullscreen和frameborder以支持全屏和边框控制</li>
* </ul>
*/
public static final PolicyFactory CMS_EDITOR_POLICY = new HtmlPolicyBuilder()
.allowElements(
"a", "b", "blockquote", "br", "caption", "cite", "code",
"col", "colgroup", "dd", "div", "dl", "dt", "em", "h1",
"h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol",
"p", "pre", "q", "small", "strike", "strong", "sub",
"sup", "table", "tbody", "td", "tfoot", "th", "thead",
"tr", "u", "ul", "span", "iframe", "video", "audio", "source")
.allowAttributes("href").onElements("a")
.allowAttributes("src", "alt", "title", "width", "height", "type", "controls").onElements("img", "iframe", "video", "audio", "source")
.allowAttributes("colspan", "rowspan").onElements("td", "th")
.allowStyling() // 允许安全的内联样式过滤危险CSS
.allowUrlProtocols("http", "https", "mailto") // 限制URL协议防止注入javascript等
.allowAttributes("data-toggle", "data-target", "data-id").onElements("*") // 支持部分自定义 data-* 属性方便前端交互
.allowAttributes("aria-label", "aria-hidden", "aria-expanded", "aria-pressed").onElements("*") // 支持无障碍辅助属性
.allowAttributes("role").onElements("*") // 支持无障碍role属性
.allowAttributes("allowfullscreen", "frameborder").onElements("iframe") // iframe 支持全屏及边框属性
.toFactory();
/**
* 纯文本过滤去除所有 HTML 标签
*/
public static String sanitizeToText(String htmlContent) {
return htmlContent == null ? null : TEXT_ONLY_POLICY.sanitize(htmlContent);
}
/**
* 简单文本过滤允许部分格式
*/
public static String sanitizeForSimpleText(String htmlContent) {
return htmlContent == null ? null : SIMPLE_TEXT_POLICY.sanitize(htmlContent);
}
/**
* 严格无脚本过滤只允许非常基础标签无任何属性
*/
public static String sanitizeStrictNoScript(String htmlContent) {
return htmlContent == null ? null : STRICT_NO_SCRIPT_POLICY.sanitize(htmlContent);
}
/**
* 评论区 HTML 过滤
*/
public static String sanitizeForComment(String htmlContent) {
return htmlContent == null ? null : COMMENT_POLICY.sanitize(htmlContent);
}
/**
* 文章编辑器 HTML 过滤
*/
public static String sanitizeForArticle(String htmlContent) {
return htmlContent == null ? null : ARTICLE_POLICY.sanitize(htmlContent);
}
/**
* 邮件富文本过滤
*/
public static String sanitizeForEmailHtml(String htmlContent) {
return htmlContent == null ? null : EMAIL_HTML_POLICY.sanitize(htmlContent);
}
/**
* CMS 编辑器过滤允许较多标签及属性
*/
public static String sanitizeForCmsEditor(String htmlContent) {
return htmlContent == null ? null : CMS_EDITOR_POLICY.sanitize(htmlContent);
}
}