对于在每个方法内部针对性的进行UTF - 8 字符编码的骚操作,已经烦乎其烦,不过确实等你烦的时候似乎就会有耐心去偷懒,好吧,进入正题。本文除了解决这个我看了一堆重复编码之后的代码之后,还有处理XSS攻击的方式,给自己留点速查的方式。
目录
请求内容编码修改部分
1、事实证明这样CV根本不能偷懒(展示例子)
应该对如下处理请求获取文件名,参数的重复代码吧,如果只有一处两处,那还勉强可以,等项目膨胀起来后,这些东西就不是这么舒服了,甚至还有点皮,本身是可以避免的,换句话吧,本身我们是可以偷懒的,至于怎么偷懒,看下面
(1)
fileName = URLEncoder.encode(fileName, "utf-8");
(2)
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
(3)
。。。
2、如果能一次修改,以后都不用CV多舒服
如果我能直接从实现类上修改你说多舒服,那我们就找找,从这个输入流找起
第一步,寻找源头实现
至于上面两个包是引入的,我们目的是寻找servlet的实现,于是走进去第三个 ServletRequestWrapper
public class ServletRequestWrapper implements ServletRequest
显然并不是我们所需要的实现,再去 HttpServletRequest 目标类下方寻找,得到
再去查看Diagrams图谱,如果是IDEA的话,右键即可看到这个选项,eclipse的话我已经很久不用了,自行查阅吧,然后目标是实现类已经找到了,显然
第二步,对其开刀整容
当然不可能在其源码内修改,come on men!
创建儿子 MultiReadHttpServletRequest ,重写该方法,当然还有其他 getReader()等
public class MultiReadHttpServletRequest {
private String requestBody;
//用于初始化时将body改编码
public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
requestBody = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
第三步,应用到项目中
自定义过滤器,就叫ChatFilter
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
MultiReadHttpServletRequest request = new MultiReadHttpServletRequest((HttpServletRequest) req);
String requestBody = request.getRequestBody();
if (StringUtils.isNotEmpty(requestBody)) {
request.setRequestBody(newRequestBody);
}
chain.doFilter(new XSSRequestWrapper(request), res);
}
①如果是传统web项目,在web.xml 对应配置过滤器,其实本身传统项目也有引入的已经帮你弄好的 CharacterEncodingFilter 可以调用
②如果是springboot项目,在你所设置的配置类或者启动类下面,所谓的配置类,是
(@Configuration 注释的,且在启动类扫描baon包内的类)
@Bean
public FilterRegistrationBean authFilterBean() {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean(new AuthFilter());
....
}
XSS应对
其实具体说明,无需我再次说,xss即为跨站脚本攻击,详情百度说明。好吧,我安利一下吧 XSS的说明
归根到底都是脚本的问题,只需要对脚本进行阻隔就会有一定的抵御性,一个在数据解码过程中处理的问题需要应对。
前端的处理预防部分,上门的超链接会有说明,这里我就不多指手画脚,让我们来谈谈后端的处理方式
主要是对于脚本以及函数的替代与过滤,使其不能得到正确的编译与执行,从而使其失效的一种方式,主要还是可以通过过滤器来进行实现处理
public class XSSRequestWrapper extends HttpServletRequestWrapper {
/**
* Avoid anything between script tags
*/
private static final Pattern scriptTagsPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
/**
* Avoid anything in a src='...' type of expression
*/
private static final Pattern scriptSrcPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern scriptSrc2Pattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
/**
* Remove any lonesome </script> tag
*/
private static final Pattern scriptTagEndPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
/**
* Remove any lonesome <script ...> tag
*/
private static final Pattern scriptTagStartPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
/**
* Avoid eval(...) expressions
*/
private static final Pattern scriptEvalPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
/**
* Avoid expression(...) expressions
*/
private static final Pattern scriptExpressionPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
/**
* Avoid javascript:... expressions
*/
private static final Pattern scriptJavascriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
/**
* Avoid vbscript:... expressions
*/
private static final Pattern scriptVbscriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
/**
* Avoid onload= expressions
*/
private static final Pattern scriptOnloadPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
/**
* Replace <> tag
*/
private static final Pattern scriptTagFlagPattern = Pattern.compile("[<>]", Pattern.CASE_INSENSITIVE);
public XSSRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (allowedWithName(parameter)) {
return values;
}
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = stripXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (allowedWithName(parameter)) {
return value;
}
return stripXSS(value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (allowedWithName(name)) {
return value;
}
return stripXSS(value);
}
public static boolean allowedWithName(String name){
return name.endsWith("_content");
}
public static String stripXSS(String value) {
if (value == null) {
return value;
}
value = scriptTagsPattern.matcher(value).replaceAll("");
value = scriptSrcPattern.matcher(value).replaceAll("");
value = scriptSrc2Pattern.matcher(value).replaceAll("");
value = scriptTagEndPattern.matcher(value).replaceAll("");
value = scriptTagStartPattern.matcher(value).replaceAll("");
value = scriptEvalPattern.matcher(value).replaceAll("");
value = scriptExpressionPattern.matcher(value).replaceAll("");
value = scriptJavascriptPattern.matcher(value).replaceAll("");
value = scriptVbscriptPattern.matcher(value).replaceAll("");
value = scriptOnloadPattern.matcher(value).replaceAll("");
value = scriptTagFlagPattern.matcher(value).replaceAll("");
return value;
}
}
从请求头,参数上隔绝,就可以比较好地让大部分通用的脚本仔没有效果,为什么我说大部分,原因大家都懂,有时候你用IE 6 ,某些转义字符也可以zh转换成尖角,我能怎么办,当然是我懒,以为不存在用IE6的骨灰级玩家,总体而言,过滤器其实是一个好东西,没有错,总结完毕。