Xss/sql/emoji过滤
在客户端(网页、APP)请求时。 有一些会点技术的坏蛋总想试试脚本、sql注入。
当然了还有无聊的人会录入emoji表情~
大家知道emoji表情mysql数据库默认的配置。存进去会报错。
原因的话。。自行去百度。。
这里emoji过滤使用的是git上开源的一个项目
https://github.com/vdurmont/emoji-java
XSS/SQL过滤是网上随便翻的一个例子~
好了。废话不多说上代码就完了 老铁!
Filter源码
WEB.XML自己配置一下啊。 不贴代码了
package com.yunxin.iambuyer.filter;
import com.vdurmont.emoji.EmojiParser;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* @Author 93年颈椎病人
* @Date 20171206
* XSS SQL EMOJI过滤
*/
public class XssAndSqlAndEmojiHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;
public XssAndSqlAndEmojiHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
//过滤XSS SQL
String value = super.getParameter(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
//过滤emoji表情
value = EmojiParser.removeAllEmojis(value);
return value;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 将容易引起xss & sql漏洞的半角字符直接替换成全角字符
*
* @param s
* @return
*/
private static String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}else{
s = stripXSSAndSql(s);
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '>':
sb.append(">");// 转义大于号
break;
case '<':
sb.append("<");// 转义小于号
break;
case '\'':
sb.append("'");// 转义单引号
break;
case '\"':
sb.append(""");// 转义双引号
break;
case '&':
sb.append("&");// 转义&
break;
case '#':
sb.append("#");// 转义#
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
/*
*//**
* 获取最原始的request
*
* @return
*//*
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
*//**
* 获取最原始的request的静态方法
*
* @return
*//*
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssAndSqlAndEmojiHttpServletRequestWrapper) {
return ((XssAndSqlAndEmojiHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}*/
/**
*
* 防止xss跨脚本攻击(替换,根据实际情况调整)
*/
public static String stripXSSAndSql(String value) {
if (value != null) {
// NOTE: It's highly recommended to use the ESAPI library and
// uncomment the following line to
// avoid encoded attacks.
// value = ESAPI.encoder().canonicalize(value);
// Avoid null characters
/** value = value.replaceAll("", "");***/
// Avoid anything between script tags
Pattern scriptPattern = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression
scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome </script> tag
scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome <script ...> tag
scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid eval(...) expressions
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid e-xpression(...) expressions
scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid javascript:... expressions
scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid vbscript:... expressions
scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid onload= expressions
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
}
return value;
}
}
接下来贴SpringMVC-JSON解析的源码
大家知道一般在SpringMVC配置JSON解析器都是
MappingJacksonHttpMessageConverter
MappingJackson2HttpMessageConverter
AbstractHttpMessageConverter
AbstractHttp2MessageConverter
这里就不多废话了。 也不贴springMVC的配置文件了。 直接贴处理代码
package com.yunxin.iambuyer.common.converter;
import com.vdurmont.emoji.EmojiParser;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.JavaType;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* Replaces Spring's {@link org.springframework.http.converter.json.MappingJacksonHttpMessageConverter}, which is
* difficult to configure for pretty-printing. This implementation enables pretty-printing easily via a setter/getter.
* <p/>
* See <a href="http://stackoverflow.com/questions/6541757/when-using-spring-mvc-for-rest-how-do-you-enable-jackson-to-pretty-print-render">
* When using Spring MVC for REST, how do you enable Jackson to pretty-print rendered JSON?</a> and the latest
* <a href="https://gist.github.com/2423129">Spring Framework incarnation supporting pretty printing</a>
* (not yet released at the time of writing).
*
* @author Les Hazlewood
*/
public class DefaultJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private ObjectMapper objectMapper = new ObjectMapper();
private boolean prefixJson = false;
private boolean prettyPrint = false;
/**
* Construct a new {@code DefaultJacksonHttpMessageConverter}.
*/
/* public DefaultJacksonHttpMessageConverter() {
super(new MediaType("application", "json", DEFAULT_CHARSET));
}*/
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
JavaType javaType = getJavaType(clazz);
return objectMapper.canDeserialize(javaType) && canRead(mediaType);
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return objectMapper.canSerialize(clazz) && canWrite(mediaType);
}
/**
* Returns the Jackson {@link JavaType} for the specific class.
* <p/>
* <p>Default implementation returns {@link org.codehaus.jackson.map.type.TypeFactory#type(java.lang.reflect.Type)}, but this can be overridden
* in subclasses, to allow for custom generic collection handling. For instance:
* <pre class="code">
* protected JavaType getJavaType(Class<?> clazz) {
* if (List.class.isAssignableFrom(clazz)) {
* return TypeFactory.collectionType(ArrayList.class, MyBean.class);
* } else {
* return super.getJavaType(clazz);
* }
* }
* </pre>
*
* @param clazz the class to return the java type for
* @return the java type
*/
/* protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.type(clazz);
}
*/
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz);
try {
Object obj = objectMapper.readValue(inputMessage.getBody(), javaType);
//转成Str进行过滤emoji
String emojiStr = super.getObjectMapper().writeValueAsString(obj);
emojiStr = EmojiParser.removeAllEmojis(emojiStr);
//在转成Obj
obj = super.getObjectMapper().readValue(emojiStr, javaType);
return obj;
} catch (JsonParseException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
}
@Override
protected boolean supports(Class<?> clazz) {
// should not be called, since we override canRead/Write instead
throw new UnsupportedOperationException();
}
@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator =
getObjectMapper().getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
try {
if (prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
getObjectMapper().writeValue(jsonGenerator, o);
} catch (JsonGenerationException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private JsonEncoding getEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (charset.name().equals(encoding.getJavaName())) {
return encoding;
}
}
}
return JsonEncoding.UTF8;
}
/* public ObjectMapper getObjectMapper() {
return objectMapper;
}*/
/**
* Sets the {@code ObjectMapper} for this view. If not set, a default
* {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control of the JSON serialization
* process. For example, an extended {@link org.codehaus.jackson.map.SerializerFactory} can be configured that provides
* custom serializers for specific types. The other option for refining the serialization process is to use Jackson's
* provided annotations on the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
*
* @param objectMapper -
*/
/* public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}*/
public boolean isPrettyPrint() {
return prettyPrint;
}
public void setPrettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
}
/**
* Indicates whether the JSON output by this view should be prefixed with "{} &&". Default is false.
* <p> Prefixing the JSON string in this manner is used to help prevent JSON Hijacking. The prefix renders the string
* syntactically invalid as a script so that it cannot be hijacked. This prefix does not affect the evaluation of JSON,
* but if JSON validation is performed on the string, the prefix would need to be ignored.
*
* @param prefixJson -
*/
/* public void setPrefixJson(boolean prefixJson) {
this.prefixJson = prefixJson;
}*/
}
实际重点就是
重写 readInternal 方法
通过MappingJacksonHttpMessageConverter类中的方法
super.getObjectMapper().writeValueAsString(obj);
将返回的对象转换成Str
在使用Emoji工具类进行替换。在转换成Obj进行返回。
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz);
try {
Object obj = objectMapper.readValue(inputMessage.getBody(), javaType);
//转成Str进行过滤emoji
String emojiStr = super.getObjectMapper().writeValueAsString(obj);
emojiStr = EmojiParser.removeAllEmojis(emojiStr);
//在转成Obj
obj = super.getObjectMapper().readValue(emojiStr, javaType);
return obj;
} catch (JsonParseException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
}
over~
祝大家过滤愉快~