fix: xss 启用后编辑器上传图片错误

pull/2/head
gaibu 2023-01-10 19:27:24 +08:00
parent d7bec143fd
commit 099754c26e
9 changed files with 34 additions and 81 deletions

View File

@ -395,11 +395,6 @@
<!-- 工作流相关结束 --> <!-- 工作流相关结束 -->
<!-- 工具类相关 --> <!-- 工具类相关 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId> <artifactId>yudao-common</artifactId>
@ -528,6 +523,12 @@
<version>${ip2region.version}</version> <version>${ip2region.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<dependency> <dependency>
<groupId>com.squareup.okio</groupId> <groupId>com.squareup.okio</groupId>

View File

@ -133,10 +133,6 @@
<artifactId>transmittable-thread-local</artifactId> <artifactId>transmittable-thread-local</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -67,6 +67,11 @@
<scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 --> <scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 -->
</dependency> </dependency>
<!-- xss -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -131,8 +131,8 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
* *
* @return XssCleaner * @return XssCleaner
*/ */
@ConditionalOnMissingBean(XssCleaner.class)
@Bean @Bean
@ConditionalOnMissingBean(XssCleaner.class)
public XssCleaner xssCleaner() { public XssCleaner xssCleaner() {
return new JsoupXssCleaner(); return new JsoupXssCleaner();
} }
@ -145,12 +145,12 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
@Bean @Bean
@ConditionalOnMissingBean(name = "xssJacksonCustomizer") @ConditionalOnMissingBean(name = "xssJacksonCustomizer")
@ConditionalOnBean(ObjectMapper.class) @ConditionalOnBean(ObjectMapper.class)
public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner, XssProperties xssProperties) { @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
// 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer在序列化时进行处理 // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer在序列化时进行处理
return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner, xssProperties)); return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
} }
private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
bean.setOrder(order); bean.setOrder(order);

View File

@ -4,6 +4,9 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist; import org.jsoup.safety.Safelist;
/**
* jsonp
*/
public class JsoupXssCleaner implements XssCleaner { public class JsoupXssCleaner implements XssCleaner {
private final Safelist safelist; private final Safelist safelist;
@ -37,19 +40,15 @@ public class JsoupXssCleaner implements XssCleaner {
} }
/** /**
* <p>
* Xss Safelist * Xss Safelist
* </p>
*
* <ul>
* Safelist#relaxed() : * Safelist#relaxed() :
* <li> style class </li> * 1. style class
* <li>a target </li> * 2. a target
* <li>img data 便 base64</li> * 3. img data 便 base64
* </ul> *
* @return Safelist * @return Safelist
*/ */
protected Safelist buildSafelist() { private Safelist buildSafelist() {
// 使用 jsoup 提供的默认的 // 使用 jsoup 提供的默认的
Safelist relaxedSafelist = Safelist.relaxed(); Safelist relaxedSafelist = Safelist.relaxed();
// 富文本编辑时一些样式是使用 style 来进行实现的 // 富文本编辑时一些样式是使用 style 来进行实现的

View File

@ -4,6 +4,7 @@ package cn.iocoder.yudao.framework.web.core.clean;
* html Xss * html Xss
*/ */
public interface XssCleaner { public interface XssCleaner {
/** /**
* Xss * Xss
* *

View File

@ -20,6 +20,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
this.xssCleaner = xssCleaner; this.xssCleaner = xssCleaner;
} }
// ============================ parameter ============================
@Override @Override
public Map<String, String[]> getParameterMap() { public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new LinkedHashMap<>(); Map<String, String[]> map = new LinkedHashMap<>();
@ -57,6 +58,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return xssCleaner.clean(value); return xssCleaner.clean(value);
} }
// ============================ attribute ============================
@Override @Override
public Object getAttribute(String name) { public Object getAttribute(String name) {
Object value = super.getAttribute(name); Object value = super.getAttribute(name);
@ -66,6 +68,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return value; return value;
} }
// ============================ header ============================
@Override @Override
public String getHeader(String name) { public String getHeader(String name) {
String value = super.getHeader(name); String value = super.getHeader(name);
@ -75,6 +78,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return xssCleaner.clean(value); return xssCleaner.clean(value);
} }
// ============================ queryString ============================
@Override @Override
public String getQueryString() { public String getQueryString() {
String value = super.getQueryString(); String value = super.getQueryString();

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.framework.web.core.json; package cn.iocoder.yudao.framework.web.core.json;
import cn.iocoder.yudao.framework.web.config.XssProperties;
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner; import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.JsonToken;
@ -12,21 +11,21 @@ import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.IOException;
/** /**
* XSS jackson * XSS jackson
* XSS
* *
* ballcat * @author Hccake
*/ */
@Slf4j @Slf4j
@AllArgsConstructor @AllArgsConstructor
public class XssStringJsonDeserializer extends StringDeserializer { public class XssStringJsonDeserializer extends StringDeserializer {
private final XssCleaner xssCleaner; private final XssCleaner xssCleaner;
private final XssProperties xssProperties;
@Override @Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.hasToken(JsonToken.VALUE_STRING)) { if (p.hasToken(JsonToken.VALUE_STRING)) {
return getCleanText(p.getText()); return xssCleaner.clean(p.getText());
} }
JsonToken t = p.currentToken(); JsonToken t = p.currentToken();
// [databind#381] // [databind#381]
@ -49,22 +48,12 @@ public class XssStringJsonDeserializer extends StringDeserializer {
if (t == JsonToken.START_OBJECT) { if (t == JsonToken.START_OBJECT) {
return ctxt.extractScalarFromObject(p, this, _valueClass); return ctxt.extractScalarFromObject(p, this, _valueClass);
} }
// allow coercions for other scalar types
// 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
// "real" scalar
if (t.isScalarValue()) { if (t.isScalarValue()) {
String text = p.getValueAsString(); String text = p.getValueAsString();
return getCleanText(text); return xssCleaner.clean(text);
} }
return (String) ctxt.handleUnexpectedToken(_valueClass, p); return (String) ctxt.handleUnexpectedToken(_valueClass, p);
} }
private String getCleanText(String text) {
if (text == null) {
return null;
}
return xssProperties.isEnable() ? xssCleaner.clean(text) : text;
}
} }

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.framework.web.core.json;
import cn.iocoder.yudao.framework.web.config.XssProperties;
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.AllArgsConstructor;
import java.io.IOException;
/**
* XSS jackson
*
* ballcat
*/
@AllArgsConstructor
public class XssStringJsonSerializer extends JsonSerializer<String> {
private final XssCleaner xssCleaner;
private final XssProperties xssProperties;
@Override
public Class<String> handledType() {
return String.class;
}
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
if (value != null) {
// 开启 Xss 才进行处理
if (xssProperties.isEnable()) {
value = xssCleaner.clean(value);
}
jsonGenerator.writeString(value);
}
}
}