解決存儲型Xss漏洞

近期做的一個項目進行滲透測試,檢測代碼存在存儲型Xss漏洞。例如在界面文本域輸入<h2>12345</h2>、<script>alter(12345)</script>這樣的值,如果程序未進行處理,會存儲至數據庫。下次回顯會出現異常界面或彈出異常框,如果進行惡意攻擊,可能後果不堪設想。

網上搜集的大部分代碼都是使用過濾器,並對請求數據進行過濾,重寫getParameter(String str)方法。因項目使用的數據結構並非字符串,而是封裝的Map,這種方法無法實現該項目的具體需求。反編譯看了jar文件,其中有方法getParameterMap()可以使用,只需進行重寫即可。

實現該功能,分三步:

  • 第一步,編寫過濾器,實現Filter接口;
  • 第二步,繼承HttpServletRequestWrapper,重寫方法getParameterMap,替換非法字符;
  • 第三步,web.xml註冊過濾器.

第一步,編寫過濾器,實現Filter接口,代碼如下:

package com.xx;

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 {
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
 
    @Override
    public void destroy() {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(new XssRequestWrappers((HttpServletRequest) request), response);
 
    }
    
}

第二步,繼承HttpServletRequestWrapper,重寫方法getParameterMap,代碼如下:

package com.xx;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
public class XssRequestWrappers extends HttpServletRequestWrapper {
    private CommonsMultipartResolver multiparResolver = new CommonsMultipartResolver();
 
    public XssRequestWrappers(HttpServletRequest request) {
        super(request);
 
        String type = request.getHeader("Content-Type");
        if (!StringUtils.isEmpty(type) && type.contains("multipart/form-data")) {
            MultipartHttpServletRequest multipartHttpServletRequest = multiparResolver.resolveMultipart(request);
            Map<String, String[]> stringMap = multipartHttpServletRequest.getParameterMap();
            if (!stringMap.isEmpty()) {
                for (String key : stringMap.keySet()) {
                    String value = multipartHttpServletRequest.getParameter(key);
                    XSSUtil.stripXSS(key);
                    XSSUtil.stripXSS(value);
                }
            }
            super.setRequest(multipartHttpServletRequest);
        }
    }
 
    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = XSSUtil.stripXSS(values[i]);
        }
        return encodedValues;
    }
 
    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        return XSSUtil.stripXSS(value);
    }
 
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        return XSSUtil.stripXSS(value);
    }
 
    @Override
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> map1 = super.getParameterMap();
		Map<String, String[]> escapseMap = new HashMap<String, String[]>();
		Set<String> keys = map1.keySet();
		for (String key : keys) {
			String[] valArr = map1.get(key);
			if (valArr != null && valArr.length > 0) {
				String[] escapseValArr = new String[valArr.length];
				for (int i = 0; i < valArr.length; i++) {
					String escapseVal = XSSUtils.striptXSS(valArr[i]);
					escapseValArr[i] = escapseVal;
				}
				escapseMap.put(key, escapseValArr);
			}
		}

		return escapseMap;
	}
    
}

此處寫了一個工具類XSSUtils,現在的做法是替換成空字符,CSDN的是進行轉義,比如文字開頭的"<"轉成&lt;。代碼如下:

package com.xx;

import java.util.regex.Pattern;

public class XSSUtils {
	 
    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;
    }
    

}

第三步,web.xml註冊過濾器,代碼如下:

        <filter>
	  <filter-name>XssFilter</filter-name>
	  <filter-class>com.xx.XSSFilter</filter-class>
	</filter>
	<filter-mapping>
	  <filter-name>XssFilter</filter-name>
	  <url-pattern>*.do</url-pattern>
	</filter-mapping>

代碼完。此代碼貼在這裏的沒加註釋,需要使用的,最好在項目中添加註釋。利人利己。

擴展:後期可對這個功能進行擴展,做成可配置的,比如請求報文的頭文件無需過濾;對不同的項目使用的不同的數據結構進行參數化;對這個需要過濾的文件格式進行參數配置,方便生產環境的突發意外情況。

思考:是否會有一種在web.xml裏配置即可解決該問題的方法,類似CharacterEncodingFilter這樣的,便於開發者操作。

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