防止XSS跨站脚本攻击:Java过滤器

简介: 防止XSS跨站脚本攻击:Java过滤器

XSS问题描述


跨站脚本(Cross site script,简称xss)是一种“HTML注入”,由于攻击的脚本多数时候是跨域的,所以称之为“跨域脚本”。

我们常常听到“注入”(Injection),如SQL注入,那么到底“注入”是什么?注入本质上就是把输入的数据变成可执行的程序语句。SQL注入是如此,XSS也如此,只不过XSS一般注入的是恶意的脚本代码,这些脚本代码可以用来获取合法用户的数据,如Cookie信息。

或者当访问者浏览网页时恶意代码会被执行或者通过给管理员发信息的方式诱使管理员浏览,从而获得管理员权限,控制整个网站

攻击者利用跨站请求伪造能够轻松地强迫用户的浏览器发出非故意的HTTP请求,如诈骗性的电汇请求、修改口令和下载非法的内容等请求。

 

风险等级:


XSS风险分析

该漏洞可能被攻击者利用窃取或操纵客户会话和Cookie,它们可能用于模仿合法用户,从而使黑客能够以合法用户身份登录系统进行渗透。可执行攻击者恶意脚本。


XSS 注入方法

在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。

在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。

在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。

在标签的 href、src 等属性中,包含 javascript: 等可执行代码。

在 onload、onerror、onclick 等事件中,注入不受控制代码。

在 style 属性和标签中,包含类似 background-image:url("javascript:…"); 的代码(新版本浏览器已经可以防范)。

在 style 属性和标签中,包含类似 expression(…) 的 CSS 表达式代码(新版本浏览器已经可以防范)。


XSS 防御

本文基于JavaWeb后端考虑,前端可参考: 前端安全系列:如何防止XSS攻击?


依赖jar

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>3.0-alpha-1</version>
  <scope>provided</scope>
</dependency>


web.xml添加过滤器

<!-- 解决xss漏洞 -->
<filter>
  <filter-name>xssFilter</filter-name>
  <filter-class>com.newcapec.cloudpay.filter.XssFilter</filter-class>
</filter>
<!-- 解决xss漏洞 -->
<filter-mapping>
  <filter-name>xssFilter</filter-name>
  <!--过滤路径-->
  <url-pattern>*</url-pattern>
</filter-mapping>


过滤器

自定义过滤器Filter拦截请求,并对请求参数进行xss过滤处理

package com.newcapec.cloudpay.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * @Title: XSS过滤器
 * @ClassName: com.newcapec.cloudpay.filter.XssFilter.java
 * @Description:
 *
 * @Copyright 2016-2018  Powered By 研发中心
 * @author: 王延飞
 * @date:  2019-01-24 16:22
 * @version V1.0
 */
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 {
        //使用包装器
        XSSRequestWrapper XSSRequestWrapper=new XSSRequestWrapper((HttpServletRequest) servletRequest);
        filterChain.doFilter(XSSRequestWrapper,servletResponse);
    }
    @Override
    public void destroy() {
    }
}


过滤器包装器

package com.newcapec.cloudpay.filter;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
 * @version V1.0
 * @Title: Request的包装类
 * @ClassName: com.newcapec.cloudpay.filter.XSSRequestWrapper.java
 * @Description: XSSRequestWrapper是Request的包装类, 用于修改Request请求,这是拦截器Interceptor所不能做到的
 * @Copyright 2016-2018  Powered By 研发中心
 * @author: 王延飞
 * @date: 2019-01-25 9:15
 */
public class XSSRequestWrapper extends HttpServletRequestWrapper {
    public XSSRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    /**
     * @param name
     * @return java.lang.String[]
     * @Title: 对数组参数进行特殊字符过滤
     * @methodName: getParameterValues
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 10:23
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = clearXss(values[i]);
        }
        return encodedValues;
    }
    /**
     * @param name
     * @return java.lang.String
     * @Title: 对参数中特殊字符进行过滤
     * @methodName: getParameter
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 10:23
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null) {
            return null;
        }
        return clearXss(value);
    }
    /**
     * @Title: 覆盖getParameterMap方法,将参数名和参数值都做xss & sql过滤
     * @methodName:  getParameterMap
     * @param
     * @return java.util.Map
     * @Description: 覆盖getParameterMap方法,将参数名和参数值都做xss & sql过滤
     * 【一般post表单请求,或者前台接收为实体需要这样处理】
     *
     * @author: 王延飞
     * @date:  2019-02-20 11:12
     */
    @Override
    public Map getParameterMap() {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        Map<String, String[]> requestMap = super.getParameterMap();
        Iterator<Entry<String, String[]>> it = requestMap.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, String[]> entry = it.next();
            if(entry.getValue().length==1){
                paramMap.put(xssEncode(entry.getKey()), xssEncode(entry.getValue()[0]));
            } else {
                String[] values = entry.getValue();
                String value = "";
                for(int i=0; i<values.length; i++){
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length()-1);
                paramMap.put(xssEncode(entry.getKey()), xssEncode(entry.getValue()[0]));
            }
        }
        return paramMap;
    }
    /**
     * @param name
     * @return java.lang.Object
     * @Title: 获取attribute, 特殊字符过滤
     * @methodName: getAttribute
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 10:24
     */
    @Override
    public Object getAttribute(String name) {
        Object value = super.getAttribute(name);
        if (value != null && value instanceof String) {
            clearXss((String) value);
        }
        return value;
    }
    /**
     * @Title: 对请求头部进行特殊字符过滤
     * @methodName: getHeader
     * @param name
     * @return java.lang.String
     * @Description:
     *
     * @author: 王延飞
     * @date: 2019-01-25 10:34
     */
    /*@Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null) {
            return null;
        }
        return clearXss(value);
    }*/
    /**
     * @param value
     * @return java.lang.String
     * @Title: 特殊字符处理(转义或删除)
     * @methodName: clearXss
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 9:16
     */
    private String clearXss(String value) {
        if (StringUtils.isEmpty(value)) {
            return value;
        }
        // 字符编码不一致,需要转换。我们系统是UTF-8编码,这里不需要
        /*try {
            value = new String(value.getBytes("ISO8859-1"), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }*/
        return XssFilterUtil.stripXss(value);
    }
   /**
     * 将容易引起xss漏洞的半角字符直接替换成全角字符 在保证不删除数据的情况下保存
     *
     * @param s
     * @return 过滤后的值
     */
    private static String xssEncode(String value) {
        if (value == null || value.isEmpty()) {
            return value;
        }
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("<","&lt;");
        value = value.replaceAll(">","&gt;");
        value = value.replaceAll("'","&apos;");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("(?i)<script.*?>.*?<script.*?>", "");
        value = value.replaceAll("(?i)<script.*?>.*?</script.*?>", "");
        value = value.replaceAll("(?i)<.*?javascript:.*?>.*?</.*?>", "");
        value = value.replaceAll("(?i)<.*?\\s+on.*?>.*?</.*?>", "");
//        value = value.replaceAll("[<>{}\\[\\];\\&]","");
        return value;
    }
}

过滤器工具类

package com.newcapec.cloudpay.filter;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @version V1.0
 * @Title:
 * @ClassName: com.newcapec.cloudpay.filter.XssFilterUtil.java
 * @Description:
 * @Copyright 2016-2018  Powered By 研发中心
 * @author: 王延飞
 * @date: 2019-01-25 9:11
 */
public class XssFilterUtil {
    private static final Logger log = LoggerFactory.getLogger(XssFilterUtil.class);
    private static List<Pattern> patterns = null;
    /**
     * @param
     * @return java.util.List<java.lang.Object                               [                               ]>
     * @Title: XSS常见攻击
     * @methodName: getXssPatternList
     * @Description: Pattern.MULTILINE(? m):在这种模式下,'^'和'$'分别匹配一行的开始和结束。此外,'^'仍然匹配字符串的开始,'$'也匹配字符串的结束。
     * 默认情况下,这两个表达式仅仅匹配字符串的开始和结束。
     * <p>
     * Pattern.DOTALL(?s) :在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。
     * 默认情况下,表达式'.'不匹配行的结束符。
     * @author: 王延飞
     * @date: 2019-01-25 9:11
     */
    private static List<Object[]> getXssPatternList() {
        List<Object[]> ret = new ArrayList<Object[]>();
        ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"</script>", Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        return ret;
    }
    /**
     * @param
     * @return java.util.List<java.util.regex.Pattern>
     * @Title: XSS常见攻击-正则表达式
     * @methodName: getPatterns
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 9:11
     */
    private static List<Pattern> getPatterns() {
        if (patterns == null) {
            List<Pattern> list = new ArrayList<Pattern>();
            String regex = null;
            Integer flag = null;
            int arrLength = 0;
            for (Object[] arr : getXssPatternList()) {
                arrLength = arr.length;
                for (int i = 0; i < arrLength; i++) {
                    regex = (String) arr[0];
                    flag = (Integer) arr[1];
                    list.add(Pattern.compile(regex, flag));
                }
            }
            patterns = list;
        }
        return patterns;
    }
    /**
     * @param value
     * @return java.lang.String
     * @Title: 处理特殊字符
     * @methodName: stripXss
     * @Description: 如果是特殊字符,策略有两种:转义或删除
     * @author: 王延飞
     * @date: 2019-01-25 9:12
     */
    public static String stripXss(String value) {
        if (StringUtils.isNotBlank(value)) {
            log.info("【XSS攻击防御】,接收字符是:{}", value);
            //
            Matcher matcher = null;
            for (Pattern pattern : getPatterns()) {
                matcher = pattern.matcher(value);
                // 匹配
                if (matcher.find()) {
                    // 删除相关字符串
                    value = matcher.replaceAll("");
                }
            }
            log.info("【XSS攻击防御】,匹配正则是:{},处理后是:{}", matcher, value);
            /**
             * 替换为转移字符,类似HtmlUtils.htmlEscape
             */
            //value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
            //删除特殊符号
            //String specialStr = "%20|=|!=|-|--|;|'|\"|%|#|+|//|/| |\\|<|>|(|)|{|}";
            if (StringUtils.isNotBlank(value)) {
                String specialStr = "%20|=|!=|-|--|;|'|\"|%|#|[+]|//|/| |\\|<|>|(|)|{|}";
                for (String str : specialStr.split("\\|")) {
                    if (value.indexOf(str) > -1) {
                        value = value.replaceAll(str, "");
                    }
                }
                log.info("【XSS攻击防御】,特殊符号处理后是:{}", value);
            }
        }
        return value;
    }
}


测试类

package com.newcapec.cloudpay.controller.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
 * @Title: Controller--测试
 * @ClassName:RsaControllerTest.java
 * @Description:
 *
 * @Copyright 2016-2017  Powered By 研发中心
 * @author: 王延飞
 * @version V1.0
 */
@Controller
@RequestMapping("/test")
public class RsaControllerTest {
    private static final Logger log = LoggerFactory.getLogger(RsaControllerTest.class);
    @RequestMapping("/testXSS")
    @ResponseBody
    public String testXSS(HttpServletRequest request){
    /*String queryString = request.getQueryString();
    log.info("【防御XSS】,客户端端请求参数是:{}", queryString);*/
        String name = request.getParameter("name");
        log.info("【防御XSS】,客户端端name是:{}", name);
        return "【防御XSS】,客户端端请求参数:" +name;
    }
}


测试结果

访问:http://localhost:8080/CloudPayment/test/testXSS.do?name=<script>alert('你被攻击了!')</script>

  • 控制台输出信息:

2019-01-25 11:50:37:512 com.newcapec.cloudpay.filter.XssFilterUtil.stripXss(XssFilterUtil.java:104) 【XSS攻击防御】,接收字符是:<script>alert('你被攻击了!')</script>

2019-01-25 11:50:37:513 com.newcapec.cloudpay.filter.XssFilterUtil.stripXss(XssFilterUtil.java:118) 【XSS攻击防御】,匹配正则是:java.util.regex.Matcher[pattern=<+\s*\w*\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|οnerrοr=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\s*=+ region=0,0 lastmatch=],处理后是:

2019-01-25 11:50:37:513 com.newcapec.cloudpay.controller.test.RsaControllerTest.testXSS(RsaControllerTest.java:64) 【防御XSS】,客户端端name是:


目录
相关文章
|
1月前
|
JavaScript 安全 前端开发
同源策略如何防止 XSS 攻击?
【10月更文挑战第31天】同源策略通过对 DOM 访问、Cookie 访问、脚本执行环境和跨源网络请求等多方面的严格限制,构建了一道坚实的安全防线,有效地防止了 XSS 攻击,保护了用户在网络浏览过程中的数据安全和隐私。
102 49
|
1月前
|
监控 Java 数据管理
java会话跟踪和拦截器过滤器
本文介绍了Web开发中的会话跟踪技术——Cookie与Session,以及过滤器(Filter)和监听器(Listener)的概念和应用。Cookie通过在客户端记录信息来识别用户,而Session则在服务器端保存用户状态。过滤器用于拦截和处理请求及响应,监听器则监控域对象的状态变化。文章详细解释了这些技术的实现方式、应用场景和主要方法,帮助开发者更好地理解和使用这些工具。
47 1
|
1月前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
101 4
|
1月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
85 2
|
1月前
|
存储 安全 Go
Web安全基础:防范XSS与CSRF攻击的方法
【10月更文挑战第25天】Web安全是互联网应用开发中的重要环节。本文通过具体案例分析了跨站脚本攻击(XSS)和跨站请求伪造(CSRF)的原理及防范方法,包括服务器端数据过滤、使用Content Security Policy (CSP)、添加CSRF令牌等措施,帮助开发者构建更安全的Web应用。
104 3
|
2月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
75 1
|
1月前
|
SQL 存储 安全
什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?
理解并防范XSS、SQL注入和CSRF攻击是Web应用安全的基础。通过采用严格的输入验证、使用安全编码实践以及实现适当的身份验证和授权机制,可以有效防止这些常见的Web攻击,保障应用程序和用户的数据安全。
42 0
|
3月前
|
存储 安全 JavaScript
XSS跨站脚本攻击详解(包括攻击方式和防御方式)
这篇文章详细解释了XSS跨站脚本攻击的概念、原理、特点、类型,并提供了攻击方式和防御方法。
604 1
|
2月前
|
Java Python
如何通过Java程序调用python脚本
如何通过Java程序调用python脚本
43 0
|
2月前
|
SQL Java 数据库连接
如何在 Java 脚本中有效地使用 JDBC
如何在 Java 脚本中有效地使用 JDBC
19 0