咸咸海风

https://github.com/xianxianhaifeng

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::

【漏洞】

跨站点脚本(XSS)攻击

 

【原因】

跨站点脚本(也称为xss)是一个漏洞,攻击者可以发送恶意代码(通常在(Javascript的形式)给另一个用户。因为浏览器无法知道脚本是否值得信任,所以它将在用户上下文中执行脚本,从而允许攻击者访问任何cookie。

 

【解决】增加敏感脚本过滤器(转一篇整理较好的实现方案: https://blog.csdn.net/yucaifu1989/article/details/61616870

使用filter过滤xss攻击,filter实现脚注入攻击过滤源码 。  先说一下实现思路:

1. 使用正则表达式的方式实现脚本过滤,这个方法准确率较高,但是可能根据不能的要求会变动;

2. 为了保证配置灵活(包括正则表达式灵活),使用xml配置文件的方式记录配置信息,配置信息包含是否开启校验、是否记录日志、是否中断请求、是否替换脚本字符等;

3. 为保证xml与正则表达式的特殊字符不冲突,使用<![CDATA[]]>标签存放正则表达式,但是在类中需要特殊处理;

4. 通过继承HttpRequestWrapper的方式实现request中header和parameter信息过滤;

5. xml解析使用dom4j,稍后会对这个工具的使用写一篇文章,暂时辛苦大家去网站查找资料

6. 使用XSSSecurityManager类实现配置信息加载和处理,XSSSecurityConfig记录匹配信息,XSSSecurityCon标识程序所需常量;

 

一共改了 7 个文件,如下:

1、XSSHttpRequestWrapper

package com.sg.security;  

import java.io.IOException;  

import java.util.Enumeration;  

import java.util.Map;  

import java.util.Set;  

import javax.servlet.ServletException;  

import javax.servlet.http.HttpServletRequest;  

import javax.servlet.http.HttpServletRequestWrapper;  

import javax.servlet.http.HttpServletResponse;  

/** 

 * @author winnie 

 * @date  

 * @describe request信息封装类,用于判断、处理request请求中特殊字符 

 */  

public class XSSHttpRequestWrapper extends HttpServletRequestWrapper {  

      

    /** 

     * 封装http请求 

     * @param request 

     */  

    public XSSHttpRequestWrapper(HttpServletRequest request) {  

        super(request);  

    }  

      

    @Override  

    public String getHeader(String name) {  

        String value = super.getHeader(name);  

        // 若开启特殊字符替换,对特殊字符进行替换  

        if(XSSSecurityConfig.REPLACE){  

            XSSSecurityManager.securityReplace(name);  

        }  

        return value;  

    }  

  

    @Override  

    public String getParameter(String name) {  

        String value = super.getParameter(name);  

        // 若开启特殊字符替换,对特殊字符进行替换  

        if(XSSSecurityConfig.REPLACE){  

            XSSSecurityManager.securityReplace(name);  

        }  

        return value;  

    }  

  

    /** 

     * 没有违规的数据,就返回false; 

     *  

     * @return 

     */  

    @SuppressWarnings("unchecked")  

    private boolean checkHeader(){  

        Enumeration<String> headerParams = this.getHeaderNames();  

        while(headerParams.hasMoreElements()){  

            String headerName = headerParams.nextElement();  

            String headerValue = this.getHeader(headerName);  

            if(XSSSecurityManager.matches(headerValue)){  

                return true;  

            }  

        }  

        return false;  

    }  

      

    /** 

     * 没有违规的数据,就返回false; 

     *  

     * @return 

     */  

    @SuppressWarnings("unchecked")  

    private boolean checkParameter(){  

        Map<String,Object> submitParams = this.getParameterMap();  

        Set<String> submitNames = submitParams.keySet();  

        for(String submitName : submitNames){  

            Object submitValues = submitParams.get(submitName);  

            if(submitValues instanceof String){  

                if(XSSSecurityManager.matches((String)submitValues)){  

                    return true;  

                }  

            }else if(submitValues instanceof String[]){  

                for(String submitValue : (String[])submitValues){  

                    if(XSSSecurityManager.matches((String)submitValue)){  

                        return true;  

                    }  

                }  

            }  

        }  

        return false;  

    }  

      

     

    /** 

     * 没有违规的数据,就返回false; 

     * 若存在违规数据,根据配置信息判断是否跳转到错误页面 

     * @param response 

     * @return 

     * @throws IOException  

     * @throws ServletException  

     */  

    public boolean validateParameter(HttpServletResponse response) throws ServletException, IOException{  

        // 开始header校验,对header信息进行校验  

        if(XSSSecurityConfig.IS_CHECK_HEADER){  

            if(this.checkHeader()){  

                return true;  

            }  

        }  

        // 开始parameter校验,对parameter信息进行校验  

        if(XSSSecurityConfig.IS_CHECK_PARAMETER){  

            if(this.checkParameter()){  

                return true;  

            }  

        }  

        return false;  

    }  

      

}  

 

 

2、XSSSecurityFilter

package com.sg.security;  

  

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;  

import javax.servlet.http.HttpServletResponse;  

  

import org.apache.log4j.Logger;  

  

  

/** 

 * @author winnie 

 * @date  

 * @describe 安全信息审核类 

 */  

public class XSSSecurityFilter implements Filter{  

  

    private static Logger logger = Logger.getLogger(XSSSecurityFilter.class);  

      

    /** 

     * 销毁操作 

     */  

    public void destroy() {  

        logger.info("XSSSecurityFilter destroy() begin");  

        XSSSecurityManager.destroy();  

        logger.info("XSSSecurityFilter destroy() end");  

    }  

  

    /** 

     * 安全审核 

     * 读取配置信息 

     */  

    public void doFilter(ServletRequest request, ServletResponse response,  

            FilterChain chain) throws IOException, ServletException {  

        // 判断是否使用HTTP  

        checkRequestResponse(request, response);  

        // 转型  

        HttpServletRequest httpRequest = (HttpServletRequest) request;  

        HttpServletResponse httpResponse = (HttpServletResponse) response;  

        // http信息封装类  

        XSSHttpRequestWrapper xssRequest = new XSSHttpRequestWrapper(httpRequest);  

          

        // 对request信息进行封装并进行校验工作,若校验失败(含非法字符),根据配置信息进行日志记录和请求中断处理  

        if(xssRequest.validateParameter(httpResponse)){  

            if(XSSSecurityConfig.IS_LOG){  

                // 记录攻击访问日志  

                // 可使用数据库、日志、文件等方式  

            }  

            if(XSSSecurityConfig.IS_CHAIN){  

                httpRequest.getRequestDispatcher(XSSSecurityCon.FILTER_ERROR_PAGE).forward( httpRequest, httpResponse);  

                return;  

            }  

        }  

        chain.doFilter(xssRequest, response);  

    }  

  

    /** 

     * 初始化操作 

     */  

    public void init(FilterConfig filterConfig) throws ServletException {  

        XSSSecurityManager.init(filterConfig);  

    }  

  

    /** 

     * 判断Request ,Response 类型 

     * @param request 

     *            ServletRequest 

     * @param response 

     *            ServletResponse 

     * @throws ServletException  

     */  

    private void checkRequestResponse(ServletRequest request,  

            ServletResponse response) throws ServletException {  

        if (!(request instanceof HttpServletRequest)) {  

            throw new ServletException("Can only process HttpServletRequest");  

  

        }  

        if (!(response instanceof HttpServletResponse)) {  

            throw new ServletException("Can only process HttpServletResponse");  

        }  

    }  

}  

 

 

3、XSSSecurityManager

package com.sg.security;  

  

import java.util.Iterator;  

import java.util.regex.Pattern;  

  

import javax.servlet.FilterConfig;  

  

import org.apache.log4j.Logger;  

import org.dom4j.DocumentException;  

import org.dom4j.Element;  

import org.dom4j.io.SAXReader;  

  

/** 

 * @author winnie 

 * @date  

 * @describe 安全过滤配置管理类,由XSSSecurityManger修改 

 */  

public class XSSSecurityManager {  

      

    private static Logger logger = Logger.getLogger(XSSSecurityManager.class);  

      

    /** 

     * REGEX:校验正则表达式 

     */  

    public static String REGEX;  

      

     /** 

     * 特殊字符匹配 

     */  

    private static Pattern XSS_PATTERN ;  

      

      

    private XSSSecurityManager(){  

        //不可被实例化  

    }  

      

    public static void init(FilterConfig config){  

        logger.info("XSSSecurityManager init(FilterConfig config) begin");  

        //初始化过滤配置文件  

        String xssPath = config.getServletContext().getRealPath("/")  

                + config.getInitParameter("securityconfig");  

          

        // 初始化安全过滤配置  

        try {  

            if(initConfig(xssPath)){  

                // 生成匹配器  

                XSS_PATTERN = Pattern.compile(REGEX);  

            }  

        } catch (DocumentException e) {  

            logger.error("安全过滤配置文件xss_security_config.xml加载异常",e);  

        }  

        logger.info("XSSSecurityManager init(FilterConfig config) end");  

    }  

      

    /** 

     * 读取安全审核配置文件xss_security_config.xml 

     * 设置XSSSecurityConfig配置信息 

     * @param path 配置文件地址 eg C:/apache-tomcat-6.0.33/webapps/security_filter/WebRoot/config/xss/xss_security_config.xml 

     * @return  

     * @throws DocumentException 

     */  

    @SuppressWarnings("unchecked")  

    public static boolean initConfig(String path) throws DocumentException {  

        logger.info("XSSSecurityManager.initConfig(String path) begin");  

        Element superElement = new SAXReader().read(path).getRootElement();  

        XSSSecurityConfig.IS_CHECK_HEADER = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHECK_HEADER));  

        XSSSecurityConfig.IS_CHECK_PARAMETER = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHECK_PARAMETER));  

        XSSSecurityConfig.IS_LOG = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_LOG));  

        XSSSecurityConfig.IS_CHAIN = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHAIN));  

        XSSSecurityConfig.REPLACE = new Boolean(getEleValue(superElement,XSSSecurityCon.REPLACE));  

  

        Element regexEle = superElement.element(XSSSecurityCon.REGEX_LIST);  

          

        if(regexEle != null){  

            Iterator<Element> regexIt = regexEle.elementIterator();  

            StringBuffer tempStr = new StringBuffer("^");  

            //xml的cdata标签传输数据时,会默认在\前加\,需要将\\替换为\  

            while(regexIt.hasNext()){  

                Element regex = (Element)regexIt.next();  

                String tmp = regex.getText();  

                tmp = tmp.replaceAll("\\\\\\\\", "\\\\");  

                tempStr.append(tmp);  

                tempStr.append("|");  

            }  

            if(tempStr.charAt(tempStr.length()-1)=='|'){  

                REGEX= tempStr.substring(0, tempStr.length()-1)+"$";  

                logger.info("安全匹配规则"+REGEX);  

            }else{  

                logger.error("安全过滤配置文件加载失败:正则表达式异常 "+tempStr.toString());  

                return false;  

            }  

        }else{  

            logger.error("安全过滤配置文件中没有 "+XSSSecurityCon.REGEX_LIST+" 属性");  

            return false;  

        }  

        logger.info("XSSSecurityManager.initConfig(String path) end");  

        return true;  

  

    }  

      

    /** 

     * 从目标element中获取指定标签信息,若找不到该标签,记录错误日志 

     * @param element 目标节点 

     * @param tagName 制定标签 

     * @return  

     */  

    private static String getEleValue(Element element, String tagName){  

        if (isNullStr(element.elementText(tagName))){  

            logger.error("安全过滤配置文件中没有 "+XSSSecurityCon.REGEX_LIST+" 属性");  

        }  

        return element.elementText(tagName);  

    }  

      

    /** 

     * 对非法字符进行替换 

     * @param text 

     * @return 

     */  

    public static String securityReplace(String text){  

        if(isNullStr(text)){  

            return text;  

        }else{  

            return text.replaceAll(REGEX, XSSSecurityCon.REPLACEMENT);  

        }  

    }  

      

    /** 

     * 匹配字符是否含特殊字符 

     * @param text 

     * @return 

     */  

    public static boolean matches(String text){  

        if(text==null){  

            return false;  

        }  

        return XSS_PATTERN.matcher(text).matches();  

    }  

      

    /** 

     * 释放关键信息 

     */  

    public static void destroy(){  

        logger.info("XSSSecurityManager.destroy() begin");  

        XSS_PATTERN = null;  

        REGEX = null;  

        logger.info("XSSSecurityManager.destroy() end");  

    }  

      

    /** 

     * 判断是否为空串,建议放到某个工具类中 

     * @param value 

     * @return 

     */  

    public static boolean isNullStr(String value){  

        return value == null || value.trim().equals("");  

    }  

}  

 

 

 

 

4、 XSSSecurityConfig

package com.sg.security;  

  

/** 

 * @author winnie 

 * 安全过滤配置信息类 

 */  

public class XSSSecurityConfig {  

      

    /** 

     * CHECK_HEADER:是否开启header校验 

     */  

    public static boolean IS_CHECK_HEADER;   

      

    /** 

     * CHECK_PARAMETER:是否开启parameter校验 

     */  

    public static boolean IS_CHECK_PARAMETER;  

      

    /** 

     * IS_LOG:是否记录日志 

     */  

    public static boolean IS_LOG;  

      

    /** 

     * IS_LOG:是否中断操作 

     */  

    public static boolean IS_CHAIN;  

      

    /** 

     * REPLACE:是否开启替换 

     */  

    public static boolean REPLACE;  

      

  

}  

 

 

 

 

5、XSSSecurityCon

package com.sg.security;  

  

/** 

 * @author winnie 

 * @date  

 * @describe 

 */  

public class XSSSecurityCon {  

  

    /** 

     * 配置文件标签 isCheckHeader 

     */  

    public static String IS_CHECK_HEADER = "isCheckHeader";  

  

    /** 

     * 配置文件标签 isCheckParameter 

     */  

    public static String IS_CHECK_PARAMETER = "isCheckParameter";  

  

    /** 

     * 配置文件标签 isLog 

     */  

    public static String IS_LOG = "isLog";  

  

    /** 

     * 配置文件标签 isChain 

     */  

    public static String IS_CHAIN = "isChain";  

  

    /** 

     * 配置文件标签 replace 

     */  

    public static String REPLACE = "replace";  

  

    /** 

     * 配置文件标签 regexList 

     */  

    public static String REGEX_LIST = "regexList";  

  

    /** 

     * 替换非法字符的字符串 

     */  

    public static String REPLACEMENT = "";  

  

    /** 

     * FILTER_ERROR_PAGE:过滤后错误页面 

     */  

    public static String FILTER_ERROR_PAGE = "/common/filtererror.jsp";  

  

}  

 

 

6、xss_security_config.xml

 

<?xml version="1.0" encoding="UTF-8"?>  

<XSSConfig>  

    <!-- 是否进行header校验 -->  

    <isCheckHeader>false</isCheckHeader>  

    <!-- 是否进行parameter校验 -->  

    <isCheckParameter>true</isCheckParameter>  

    <!-- 是否记录日志 -->  

    <isLog>true</isLog>  

    <!-- 是否中断请求 -->  

    <isChain>false</isChain>  

    <!-- 是否开启特殊字符替换 -->  

    <replace>true</replace>  

    <!-- 是否开启特殊url校验 -->  

    <isCheckUrl>true</isCheckUrl>  

    <regexList>  

        <!-- 匹配含有字符: alert( ) -->  

        <regex><![CDATA[.*[A|a][L|l][E|e][R|r][T|t]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: window.location = -->  

        <regex><![CDATA[.*[W|w][I|i][N|n][D|d][O|o][W|w]\\.[L|l][O|o][C|c][A|a][T|t][I|i][O|o][N|n]\\s*=.*]]></regex>  

        <!-- 匹配含有字符:style = x:ex pression ( ) -->  

        <regex><![CDATA[.*[S|s][T|t][Y|y][L|l][E|e]\\s*=.*[X|x]:[E|e][X|x].*[P|p][R|r][E|e][S|s]{1,2}[I|i][O|o][N|n]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: document.cookie -->  

        <regex><![CDATA[.*[D|d][O|o][C|c][U|u][M|m][E|e][N|n][T|t]\\.[C|c][O|o]{2}[K|k][I|i][E|e].*]]></regex>  

        <!-- 匹配含有字符: eval( ) -->  

        <regex><![CDATA[.*[E|e][V|v][A|a][L|l]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: unescape() -->  

        <regex><![CDATA[.*[U|u][N|n][E|e][S|s][C|c][A|a][P|p][E|e]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: execscript( ) -->  

        <regex><![CDATA[.*[E|e][X|x][E|e][C|c][S|s][C|c][R|r][I|i][P|p][T|t]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: msgbox( ) -->  

        <regex><![CDATA[.*[M|m][S|s][G|g][B|b][O|o][X|x]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: confirm( ) -->  

        <regex><![CDATA[.*[C|c][O|o][N|n][F|f][I|i][R|r][M|m]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: prompt( ) -->  

        <regex><![CDATA[.*[P|p][R|r][O|o][M|m][P|p][T|t]\\s*\\(.*\\).*]]></regex>  

        <!-- 匹配含有字符: <script> </script> -->  

        <regex><![CDATA[.*<[S|s][C|c][R|r][I|i][P|p][T|t]>.*</[S|s][C|c][R|r][I|i][P|p][T|t]>.*]]></regex>  

        <!-- 匹配含有字符: 含有一个符号: "  -->  

        <regex><![CDATA[[.&[^\"]]*\"[.&[^\"]]*]]></regex>  

        <!-- 匹配含有字符: 含有一个符号: '  -->  

        <regex><![CDATA[[.&[^']]*'[.&[^']]*]]></regex>  

        <!-- 匹配含有字符: 含有回车换行 和 <script> </script> -->  

        <regex><![CDATA[[[.&[^a]]|[|a|\n|\r\n|\r|\u0085|\u2028|\u2029]]*<[S|s][C|c][R|r][I|i][P|p][T|t]>.*</[S|s][C|c][R|r][I|i][P|p][T|t]>[[.&[^a]]|[|a|\n|\r\n|\r|\u0085|\u2028|\u2029]]*]]></regex>  

    </regexList>  

</XSSConfig>  

 

 

7、web.xml配置

<?xml version="1.0" encoding="UTF-8"?>  

<web-app version="2.5"   

    xmlns="http://java.sun.com/xml/ns/javaee"   

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  

  <welcome-file-list>  

    <welcome-file>index.jsp</welcome-file>  

  </welcome-file-list>  

    <!-- 信息安全审核 -->  

    <filter>  

        <filter-name>XSSFiler</filter-name>  

        <filter-class>  

            com.sg.security.XSSSecurityFilter  

        </filter-class>  

        <init-param>  

            <param-name>securityconfig</param-name>  

            <param-value>  

                /WebRoot/config/xss/xss_security_config.xml  

            </param-value>  

        </init-param>  

    </filter>  

    <!-- 拦截请求类型 -->  

    <filter-mapping>  

        <filter-name>XSSFiler</filter-name>  

        <url-pattern>*.jsp</url-pattern>  

    </filter-mapping>  

    <filter-mapping>  

        <filter-name>XSSFiler</filter-name>  

        <url-pattern>*.do</url-pattern>  

    </filter-mapping>  

</web-app>

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.5"   
  3.     xmlns="http://java.sun.com/xml/ns/javaee"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  7.   <welcome-file-list>  
  8.     <welcome-file>index.jsp</welcome-file>  
  9.   </welcome-file-list>  
  10.     <!-- 信息安全审核 -->  
  11.     <filter>  
  12.         <filter-name>XSSFiler</filter-name>  
  13.         <filter-class>  
  14.             com.sg.security.XSSSecurityFilter  
  15.         </filter-class>  
  16.         <init-param>  
  17.             <param-name>securityconfig</param-name>  
  18.             <param-value>  
  19.                 /WebRoot/config/xss/xss_security_config.xml  
  20.             </param-value>  
  21.         </init-param>  
  22.     </filter>  
  23.     <!-- 拦截请求类型 -->  
  24.     <filter-mapping>  
  25.         <filter-name>XSSFiler</filter-name>  
  26.         <url-pattern>*.jsp</url-pattern>  
  27.     </filter-mapping>  
  28.     <filter-mapping>  
  29.         <filter-name>XSSFiler</filter-name>  
  30.         <url-pattern>*.do</url-pattern>  
  31.     </filter-mapping>  
  32. </web-app> 
posted on 2019-09-11 16:36  咸咸海风  阅读(1646)  评论(0编辑  收藏  举报