存儲型跨站腳本漏洞,過濾特殊字符, SpringMvc防範XSS攻擊。

公司最近在搞測試,請的第三方測試機構。。。一頓操作猛如虎。。。

記錄一次現在很多項目都容易忽略的存儲型跨站腳本漏洞處理,就是XSS攻擊漏洞,只要有用戶輸入的地方,就可能會有XSS漏洞,比如我們在留言板,或者發佈文章的地方輸入:

  1. <script>alert(1);</script>

當這條數據被存儲到數據庫,並且在頁面上再次顯示的時候,就會彈窗輸出“1”,這只是簡單的script腳本,嚴重的可能會出現reload到釣魚網站,讓用戶輸入密碼、sql注入,獲取數據或者刪庫、獲取當前的cookie,並將數據發送到指定郵箱等問題,後果不堪設想。

很多網站爲了避免XSS的攻擊,對用戶的輸入都採取了過濾,最常見的就是對<>轉換,轉換成大於號,小於號,驗證了一下CSDN的留言板,是將<script>轉換成[removed],經過轉換以後<>雖然可在正確顯示在頁面上,但是已經不能構成代碼語句了。

我們的項目是用的SpringMvc框架,所以就記錄在SpringMvc項目中如何解決XSS攻擊問題:

主要邏輯:

  1. 建一個過濾器,過濾來自頁面的請求
  2. 在過濾器中,將原本的request替換成我們自己包裝好的request
  3. 我們寫一個request的裝飾類,裏面重寫獲取參數的方法,對參數進行轉義和替換

具體代碼:

  1. reqeust包裝類,重寫getParameter()等方法,這裏面對參數的轉義和替換,根據實際需求更改

    package com.study.util;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.regex.Pattern;
    
    
    /**
     * @Auther: 安靜讀書
     * @Date: 2019/9/26 09:34
     * @Description:重寫getParameter方法
     */
    public class XssRequestWrapper extends HttpServletRequestWrapper {
    
        private HttpServletRequest request;
    
        public XssRequestWrapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
    
        /**
         * 重寫getParameter方法
         */
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            if (value == null) {
                return null;
            }
            value = format(value);
            return value;
        }
    
        /**
         * 重寫getParameterMap
         */
        @Override
        @SuppressWarnings("unchecked")
        public Map<String, String[]> getParameterMap() {
            HashMap<String, String[]> paramMap = new HashMap<>(super.getParameterMap());
            paramMap = (HashMap<String, String[]>) paramMap.clone();
    
            for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) {
                Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) iterator.next();
                String [] values = entry.getValue();
                for (int i = 0; i < values.length; i++) {
                    if(values[i] instanceof String){
                        values[i] = format(values[i]);
                    }
                }
                entry.setValue(values);
            }
            return paramMap;
        }
    
        /**
         * 重寫getParameterValues
         */
        @Override
        public String[] getParameterValues(String name) {
            String[] encodedValues = null;
            String[] values = super.getParameterValues(name);
            if(values != null){
                int count = values.length;
                encodedValues = new String[count];
                for (int i = 0; i < count; i++) {
                    encodedValues[i] = format(values[i]);
                }
            }
            return encodedValues;
        }
    
        /**
         * 重寫getHeader
         */
        @Override
        public String getHeader(String name) {
            return format(super.getHeader(name));
        }
    
        public String filter(String message) {
            if (message == null)
                return (null);
            message = format(message);
            return message;
        }
    
        /**
         *  @desc 統一處理特殊字符的方法,替換掉sql和js的特殊字符
         *  @param name 要替換的字符
         */
        private String format(String name) {
            return xssEncode(name);
        }
    
        /**
         * 將容易引起xss & sql漏洞的半角字符直接替換成全角字符
         *
         * @param s
         * @return
         */
        private static String xssEncode(String s) {
            if (s == null || s.isEmpty()) {
                return s;
            }else{
                s = stripXSSAndSql(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跨腳本攻擊(替換,根據實際情況調整)
         */
        public static String stripXSSAndSql(String value) {
            if (value != null) {
                Pattern scriptPattern = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
                // Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression
                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("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
                // Remove any lonesome <script ...> tag
                scriptPattern = Pattern.compile("<[\r\n| | ]*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 e-xpression(...) expressions
                scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
                // Avoid javascript:... expressions
                scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
                // Avoid vbscript:... expressions
                scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
                // Avoid onload= expressions
                scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
                value = scriptPattern.matcher(value).replaceAll("");
            }
            return value;
        }
    }
    

     

  2. 過濾器類

    package com.study.util;
    
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @Auther: 安靜讀書
     * @Date: 2019/9/26 09:33
     * @Description: 處理xss攻擊的過濾器
     */
    public class RequestFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            // 將request通過自定義的裝飾類進行裝飾
            com.study.om.util.XssRequestWrapper xssRequest = new com.study.om.util.XssRequestWrapper((HttpServletRequest) request);
            filterChain.doFilter(xssRequest, response);
        }
    }
    

     

  3. 在web.xml中加入自己的過濾器,可以加到filter後面

    <!-- xss注入過濾器 -->
        <filter>
            <filter-name>xssRequestFilter</filter-name>
            <filter-class>com.study.util.RequestFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>xssRequestFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    完成之後可以啓動項目進行驗證,我們找一個有輸入框的地方,輸入<script>alert(1);</script>,不會有顯示,而且也沒有進行保存,這樣就可以避免存儲型腳本了,解決xss攻擊。

當然,如果項目不是SpringMVC的模式,而且輸入的地方很少,也可以加一層簡單的過濾工具類:

package com.study.util;

import java.util.regex.Pattern;

/**
 * @Auther: 安靜讀書
 * @Date: 2019/9/26 14:17
 * @Description:解決存儲型跨站腳本漏洞,過濾特殊字符
 */
public class XSSUtil {
    public static String striptXSS(String value) {
        if (value != null) {
            value = value.replaceAll("", "");
            Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
            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("");
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile(".*<.*", Pattern.CASE_INSENSITIVE );
            value = scriptPattern.matcher(value).replaceAll("");
        }
        return value;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章