XSS跨域攻击和SQL注入解决方案

1.配置web.xml的filter

<filter>
		<filter-name>xssFilter</filter-name>
		<display-name>xssFilter</display-name>
		<filter-class>com.boco.framework.common.web.struts.XSSFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>xssFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>antiSqlFilter</filter-name>
		<display-name>antiSqlFilter</display-name>
		<filter-class>com.boco.framework.security.filter.AntiSqlInjectionfilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>antiSqlFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>

2.新建一个XSSFilter

public class XSSFilter implements Filter {

  public void init(FilterConfig filterConfig) throws ServletException {
  }


  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {

    XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
        (HttpServletRequest) request);
    chain.doFilter(xssRequest, response);
  }


  public void destroy() {
  }

}


public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public String escape(String 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();
    }
    
    /**
     * 将容易引起xss漏洞的半角字符直接替换成全角字符
     * 
     * @param s
     * @return
     */
    public String xssEncode(String s) {
        if (s == null || s.isEmpty()) {
            return s;
        }
        
        String result = stripXSS(s);
        //导致委托代理中委托书富文本中'<'被替换为全角'<'不能生产显示,故取消此半角转全角功能
//        if (null != result) {
//            result = escape(result);
//        }
        
        return result;
    }
    
    private String stripXSS(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("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            // Avoid anything in a src='...' type of expression
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            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("</script>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            // Remove any lonesome <script ...> tag
            scriptPattern = Pattern.compile("<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 expression(...) expressions
            scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            // Avoid javascript:... expressions
            scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            // Avoid vbscript:... expressions
            scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            // Avoid οnlοad= expressions
            scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            
            scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            
            scriptPattern = Pattern.compile("</iframe>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            // Remove any lonesome <script ...> tag
            scriptPattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
        }
        return value;
    }
    
    public XssHttpServletRequestWrapper(HttpServletRequest request) {  
        super(request);  
    }  
  
    @Override  
    public String getParameter(String name) {  
        return xssEncode(super.getParameter(name));  
    }  
  
    @Override  
    public String getHeader(String name) {  
        return xssEncode(super.getHeader(name));  
    }  
  
    @Override  
    public String[] getParameterValues(String name) {  
        String[] values = super.getParameterValues(name);  
        if (values == null) {  
            return null;  
        }  
        String[] newValues = new String[values.length];  
  
        for (int i = 0; i < values.length; i++) {  
            newValues[i] = xssEncode(values[i]);  
        }  
  
        return newValues;  
    }
}

3.新建一个filter过滤相关字符

在获取请求参数时POST和GET获取方式存在差异

一 获取URL:
getRequestURL()

二 获取参数列表:

1.getQueryString()

只适用于GET,比如客户端发送http://localhost/testServlet?a=b&c=d&e=f,通过request.getQueryString()得到的是a=b&c=d&e=f.


2.getParameter()
GET和POST都可以使用
但如果是POST请求要根据<form>表单提交数据的编码方式来确定能否使用.
当编码方式是(application/x- www-form-urlencoded)时才能使用.
这种编码方式(application/x-www-form-urlencoded)虽然简单,但对于传输大块的二进制数据显得力不从心.
对于传输大块的二进制数这类数据,浏览器采用了另一种编码方式("multipart/form-data"),这时就需要使用下面的两种方法.

3.getInputStream()
4.getReader()
上面两种方法获取的是Http请求包的包体,因为GET方式请求一般不包含包体.所以上面两种方法一般用于POST请求获取参数.

需要注意的是:
request.getParameter()、 request.getInputStream()、request.getReader()这三种方法是有冲突的,因为流只能被读一次。
比如:
当form表单内容采用 enctype=application/x-www-form-urlencoded编码时,先通过调用request.getParameter()方法得到参数后,
再调用request.getInputStream()或request.getReader()已经得不到流中的内容,
因为在调用 request.getParameter()时系统可能对表单中提交的数据以流的形式读了一次,反之亦然。

当form表单内容采用 enctype=multipart/form-data编码时,即使先调用request.getParameter()也得不到数据,
所以这时调用request.getParameter()方法对 request.getInputStream()或request.getReader()没有冲突,
即使已经调用了 request.getParameter()方法也可以通过调用request.getInputStream()或request.getReader()得到表单中的数据,
而request.getInputStream()和request.getReader()在同一个响应中是不能混合使用的,如果混合使用就会抛异常。

获取POST请求参数方法一:

ServletInputStream ris = req.getInputStream();  
    StringBuilder content = new StringBuilder();  
    byte[] b = new byte[1024];  
    int lens = -1;  
    while ((lens = ris.read(b)) > 0) {  
        content.append(new String(b, 0, lens));  
    }  
    String strcont = content.toString();// 内容  


/**
 * 过滤器 
 * 功能:<br>
 * 1-防SQL注入<br>
 * 2-防跨域(XSS)<br>
 * 3-request参数值去字符两边空白字符(暂时只针对查询参数)<br>
 * 4-针对数量和单价和金额最大值进行过滤<br>
 * @author szj
 * @update 2016-07-14
 */
public class AntiSqlInjectionfilter implements Filter {
    private static Pattern SCRIPT_PATTERN = Pattern.compile("<script.*>.*<\\/script\\s*>");
    
    private CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    
//    private static Pattern HTML_PATTERN = Pattern.compile("<[^>]+>");
    
    public AntiSqlInjectionfilter() {
    }
    
    public void destroy() {
    }
    
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain) throws IOException,
                                           ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
//        HttpServletResponse resp = (HttpServletResponse) response;
        
//        response.setCharacterEncoding("GBK");
//        resp.setHeader("Pragma", "no-cache");
//        resp.setHeader("Cache-Control", "no-cache");
//        resp.setHeader("Cache-Control", "no-store");
//        resp.setDateHeader("Expires", 0);
        //判断enctype属性是否为multipart/form-data  
        String contentType = req.getContentType();//获取请求的content-type
        String requestURI = req.getRequestURI();
        
        if(StringUtils.isNotBlank(contentType) && contentType.contains("multipart/form-data") &&
                StringUtils.isNotBlank(requestURI) && !requestURI.contains("progressBarResponse.do")){//文件上传请求 *特殊请求(除去Excel导入的提交方式)
            MultipartHttpServletRequest multiReq = multipartResolver.resolveMultipart(req);
            request = multiReq;//将转化后的reuqest赋值到过滤链中的参数 *重要
        }
        //是否需要对参数进行去空格处理
        boolean isTrimFalg = false;
        try {
            
            String qs = req.getQueryString();
            try {//URL反编码,防止req.getParameterMap()失效
                if(!StringUtils.isNotEmpty(qs)){
                    qs = "";
                }
                //获取POST 请求当中的参数
                ServletInputStream ris = req.getInputStream();
                if(ris!=null){
                    StringBuilder content = new StringBuilder();  
                    byte[] b = new byte[1024];  
                    int lens = -1;  
                    while ((lens = ris.read(b)) > 0) {  
                        content.append(new String(b, 0, lens));  
                    }
                    qs += content.toString();// 内容
                }
                
                
                qs = URLDecoder.decode(qs,"UTF-8");
            }
            catch (Exception e) {
                throw new Exception("请求参数中包含了非法字符。");
            }
            
            if(qs.contains("<") || qs.contains(">") 
                    || qs.contains("%") 
                    || qs.contains("'")
                    || qs.contains(";") 
                    || qs.contains("$")
                    || qs.contains("\"")
                    || qs.contains("+")
                    || qs.contains("(")
                    || qs.contains(")")
                    || qs.contains("cr")){
                
                throw new Exception("请求参数中包含了非法字符。");
            }
            
            Map paramMap = req.getParameterMap();
            int flag = 0;
            String lowStr = null;
            Set keSet = paramMap.entrySet();
            for (Iterator itr = keSet.iterator(); itr.hasNext();) {
                Map.Entry me = (Map.Entry) itr.next();
                Object ok = me.getKey();
                if(ok  instanceof String) {
                    String key = (String)ok;
                    if(key.toUpperCase().contains("MAIL")){
                        continue;
                    }
                    if(key.contains("Parameter")){//暂时只针对查询参数去空格处理
                        isTrimFalg=true;
                    }
                }
                Object ov = me.getValue();
                String[] value = new String[1];
                if (ov instanceof String[]) {
                    value = (String[]) ov;
                }
                else {
                    value[0] = ov.toString();
                }
                for (int k = 0; k < value.length; k++) {
                    //System.out.println("key:"+ok+" "+"value:"+value[k]);

                    lowStr = URLDecoder.decode(value[k].toLowerCase(),"UTF-8");
                    // 过滤html标签 交办中使用了html脚本,不能屏蔽。否则无法交办
//                    Matcher mHtml = HTML_PATTERN.matcher(lowStr);
//                    if (mHtml.find()) {
//                        throw new Exception("请求参数中包含了非法脚本。");
//                    }
                    //过滤html相关标签
                    if(lowStr.contains("confirm") || lowStr.contains("src=")){
                        throw new Exception("请求参数中包含了非法html脚本。");
                    }
                    
                    // 过滤script脚本
                    Matcher m = SCRIPT_PATTERN.matcher(lowStr);
                    if (m.find()) {
                        throw new Exception("请求参数中包含了非法的script脚本。");
                    }
                    
                    //  过滤XSS跨域脚本
                    if(stripXSS(lowStr)){
                        throw new Exception("请求参数中包含了非法的XSS跨站点脚本。");
                    }
                    
                    // 过滤特殊字符
                    if (lowStr.contains("|") 
                            || lowStr.contains("&") 
                            || lowStr.contains(";")
                            || lowStr.contains("$")
                            || lowStr.contains("%")
                            || lowStr.contains("@")
                            || lowStr.contains("\"")
//                            || lowStr.contains("+")
                            || lowStr.contains("\'")
//                            || lowStr.contains("\\")
                            || lowStr.contains("'")
//                            || lowStr.contains("<")
//                            || lowStr.contains(">")
//                            || lowStr.contains("(")
//                            || lowStr.contains(")")
                            || lowStr.contains("cr")
//                            || lowStr.contains(",")
//                            || lowStr.contains(".")
                            
                            || isContains4Blank(lowStr,"lf",1)
                            
                            || lowStr.contains("document")
                            || lowStr.contains("eval")
                            ) {
                        throw new Exception("拒绝访问:参数【"+lowStr+"】含特殊字符。");
                    }
                    
                    // 过滤sql转换函数
                    if (lowStr.contains("ascii(") || lowStr.contains("ascii (")
                        || lowStr.contains("chr(")
                        || lowStr.contains("chr (")) {
                        throw new Exception("请求参数中包含了非法的sql脚本转换函数关键字。");
                    }
                    // 过滤sql关键字
//                    if (lowStr.contains("alter ") 
//                        || lowStr.contains("create ")
//                        || lowStr.contains("truncate ")
//                        || lowStr.contains("drop ")
//                        || lowStr.contains("lock table")
//                        || lowStr.contains("insert ")
//                        || lowStr.contains("update ")
//                        || lowStr.contains("delete ")
//                        || lowStr.contains("select ")
//                        || lowStr.contains("grant ")) {
//                        throw new Exception("请求参数中包含了非法的sql脚本关键字。");
//                    }
                    if (isContains4Blank(lowStr,"alter",1) 
                        || isContains4Blank(lowStr,"create",1)
                        || isContains4Blank(lowStr,"truncate",1)
                        || isContains4Blank(lowStr,"drop",1)
                        || isContains4Blank(lowStr,"lock",1)
                        || isContains4Blank(lowStr,"table", 2)
                        || isContains4Blank(lowStr,"insert",1)
                        || isContains4Blank(lowStr,"update",1)
                        || isContains4Blank(lowStr,"delete",1)
                        || isContains4Blank(lowStr,"select",1)
                        || isContains4Blank(lowStr,"grant",1)
                        || isContains4Blank(lowStr,"like",1)
                        || isContains4Blank(lowStr,"or", 3)
                        || isContains4Blank(lowStr,"and", 3)) {
                        throw new Exception("请求参数中包含了非法的sql脚本关键字。");
                    }
                    
                    //针对数量和单价和金额最大值进行过滤
                    String key = (String) ok;
                    if (key.toLowerCase().contains("amount") 
                        || key.toLowerCase().contains("price")
                        || key.toLowerCase().contains("money")
                        || key.toLowerCase().contains("presumw")) {
                        Double db = null;
                        try {
                            db = Double.parseDouble(lowStr);
                        }
                        catch (Exception e) {
                        }
                        if (db != null && db.longValue() > 99999999999999l) {
                            throw new Exception("您录入的数字【" + lowStr + "】过大。请修改!");
                        }
                    }
                }
            }
            
            // 暂时只针对查询参数去空格处理
            if (isTrimFalg) {
                ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) request);
                chain.doFilter(requestWrapper, response);
            }else{
                chain.doFilter(request, response);
            }
        } 
        catch (Exception e) {
            e.printStackTrace();
            String msg = e.getMessage();
            //2015.12.28 错误提示优化
            if(msg.contains("Exception:")){
                msg = msg.split("Exception:")[1].toString();
            }
            request.setAttribute("message", msg);
            request.getRequestDispatcher("/error.jsp")
                   .forward(request, response);
        }
    }
    
    /**
     * XSS跨域校验
     * @param value 校验值
     * @return 是否存在跨域脚本:true是,false否
     */
    private boolean stripXSS(String value) {
        String cleanValue = null;
        int rsFlag = 0;
        if (value != null) {
            cleanValue = Normalizer.normalize(value, Normalizer.Form.NFD);
            
            // Avoid null characters
            cleanValue = cleanValue.replaceAll("\0", "");
            
            // Avoid anything between script tags
            Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>",
                                                    Pattern.CASE_INSENSITIVE);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            
            // Avoid anything in a src='...' type of expression
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Remove any lonesome </script> tag
            scriptPattern = Pattern.compile("</script>",
                                            Pattern.CASE_INSENSITIVE);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Remove any lonesome <script ...> tag
            scriptPattern = Pattern.compile("<script(.*?)>",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Avoid eval(...) expressions
            scriptPattern = Pattern.compile("eval\\((.*?)\\)",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Avoid expression(...) expressions
            scriptPattern = Pattern.compile("expression\\((.*?)\\)",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Avoid javascript:... expressions
            scriptPattern = Pattern.compile("javascript:",
                                            Pattern.CASE_INSENSITIVE);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Avoid vbscript:... expressions
            scriptPattern = Pattern.compile("vbscript:",
                                            Pattern.CASE_INSENSITIVE);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            scriptPattern = Pattern.compile("jscript:",
                                            Pattern.CASE_INSENSITIVE);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
            // Avoid οnlοad= expressions
            scriptPattern = Pattern.compile("onload(.*?)=",
                                            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                                                    | Pattern.DOTALL);
            if (scriptPattern.matcher(cleanValue).find()) {
                rsFlag++;
            }
        }
        return rsFlag > 0;
    }
    
    /**
     * 判断是否包含测试字符+空白的情况
     * @param str url参数
     * @param testString 测试是否包含的字符串
     * @param 1 检测后一位是否是空白;2 检测前一位是否是空白;3 检测前、后一位是否是空白
     * @return
     */
    private boolean isContains4Blank(String str,String testString,int type){
        if(str.contains(testString)){
            String[] ss = str.split(testString);
            
            if(type == 1){
                for(int i=1;i<ss.length;i++){
                    char c = ss[i].charAt(0);
                    if(c <= ' ' || c == '\''){
                        return true;
                    }
                }
            }
            
            if(type == 2){
                for(int i=0;i<ss.length-1;i++){
                    if(ss[i] != null && ss[i].length()>0){
                        char c = ss[i].charAt(ss[i].length()-1);
                        if(c <= ' ' || c == '\''){
                            return true;
                        }
                    }
                }
            }
            
            if(type == 3){
                for(int i=0;i<ss.length-1;i++){
                    if(ss[i] != null && ss[i].length()>0){
                        char c = ss[i].charAt(ss[i].length()-1);
                        char c2 = ss[i+1].charAt(0);
                        if((c <= ' ' || c == '\'')
                            && (c2 <= ' ' || c2 == '\'')){
                            return true;
                        }
                    }
                }
            }
            
        }
        
        return false;
    }
    
    public void init(FilterConfig fConfig) throws ServletException {
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值