web 安全系列-03-XSS 的解决方案及一些绕过方式
2020年8月9日大约 5 分钟
XSS 解决方案
前言
我们先看一篇比较常见的解决方式:
实现
拦截器配置
- web.xml
XssSqlFilter
com.xxx.XssFilter
XssSqlFilter
/*
REQUEST
拦截器实现
- XssFilter
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class XssFilter implements Filter {
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//调用重写后的请求
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
}
}
- XssHttpServletRequestWrapper.java
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper{
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
@Override
public Map getParameterMap(){
Set names = super.getParameterMap().keySet();
Map parameterMap = new HashMap(names.size());
for (String name : names) {
String [] values = getParameterValues(name);
String [] newValues = new String [values.length];
for(int i=0;i &";
String inj_stra[] = inj_str.split(" ");
for (int i = 0; i ", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
value = value.replaceAll("iframe", "");
value = value.replaceAll("img", "");
return value;
}
}
ps: 这里也包含了 SQL 注入的过滤。
绕过的方式
大小写
这个绕过方式的出现是因为网站仅仅只过滤了 `` 标签,而没有考虑标签中的大小写并不影响浏览器的解释所致。
具体的方式就像这样:
http://192.168.1.102/xss/example2.php?name=alert("hey!")
利用过滤后返回语句再次构成攻击语句来绕过
这个字面上不是很好理解,用实例来说。
如下图,在这个例子中我们直接敲入script标签发现返回的网页代码中script标签被去除了,但其余的内容并没有改变。
http://192.168.1.102/xss/example3.php?name=pt>alert("hey!")pt>
ps: 这其实是一个非常巧妙的方式,当然我们后续会统一给出解决方案。
并不是只有script标签才可以插入代码
在这个例子中,我们尝试了前面两种方法都没能成功,原因在于script标签已经被完全过滤,但不要方,能植入脚本代码的不止script标签。
例如这里我们用 `` 标签做一个示范。
http://192.168.1.102/xss/example4.php?name=
就可以再次愉快的弹窗。
原因很简单,我们指定的图片地址根本不存在也就是一定会发生错误,这时候onerror里面的代码自然就得到了执行。
以下列举几个常用的可插入代码的标签。
当用户鼠标移动时即可运行代码
编码脚本代码绕过关键字过滤
有的时候,服务器往往会对代码中的关键字(如alert)进行过滤,这个时候我们可以尝试将关键字进行编码后再插入,不过直接显示编码是不能被浏览器执行的,我们可以用另一个语句eval()来实现。
eval()会将编码过的语句解码后再执行,简直太贴心了。
例如alert(1)编码过后就是
\u0061\u006c\u0065\u0072\u0074(1)
攻击语句:
http://192.168.1.102/xss/example5.php?name=eval(\u0061\u006c\u0065\u0072\u0074(1))
解决方案
思考
如果去穷举各种各样的 Onxxx 函数肯定是不现实的。
我们只需要处理最核心的 `` 之类的特殊字符即可。
这样实现就可以变得非常简单。
实现
- 配置拦截器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(new XssFilterWrapper((HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
}
}
XssFilterWrapper 实现如下:
import org.apache.commons.lang.StringUtils;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssFilterWrapper extends HttpServletRequestWrapper {
public XssFilterWrapper(HttpServletRequest request) {
super(request);
}
/**
* 对数组参数进行特殊字符过滤
*/
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
String[] newValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
newValues[i] = HtmlUtils.htmlEscape(values[i]);
}
return newValues;
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (StringUtils.isNotBlank(value)) {
return HtmlUtils.htmlEscape(value);
}
return value;
}
}
非常的简单粗暴,就是一个过滤。
拓展阅读
参考资料
贡献者
binbin.hou