spring mvc xss filter

Spring MVC Xss Filter

       

       XSS (Cross Site Scripting): 跨站腳本攻擊, 是Web程序中最常見的漏洞。指攻擊者在網頁中嵌入客戶端腳本(例如JavaScript), 當用戶瀏覽此網頁時,腳本就會在用戶的瀏覽器上執行,從而達到攻擊者的目的.  比如獲取用戶的Cookie,導航到惡意網站,攜帶木馬等。

          一般現代應用MVC實現spring mvc的實現比較廣泛,因此,本文章描述spring mvc 的實現xss filter。

第一步

        在web.xml中配置filter:

<!-- Xss-html-filter -->
	<filter>
        <filter-name>xssHttpServletFilter</filter-name>
        <filter-class>com.x.y.filter.XssHttpServletFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>xssHttpServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

第二步

        創建過濾器
import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 包裝原生servlet對象,處理xss問題
 * 
 */
public class XssHttpServletFilter  extends OncePerRequestFilter {

	private static final Logger LOGGER = LoggerFactory.getLogger(XssHttpServletFilter.class);

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		try {
			XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
			chain.doFilter(xssRequest, response);
		} catch (Exception e) {
			LOGGER.error("Xss過濾器,包裝request對象失敗");
			chain.doFilter(request, response);
		}				
	}
	
}


第三步

        創建request包裝對象
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 請求包裝對象 處理Xss
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

	HttpServletRequest orgRequest = null;

	public XssHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
		orgRequest = request;
	}

	/**
	 * 覆蓋getParameter方法,將參數名和參數值都做xss過濾
	 */
	@Override
	public String getParameter(String name) {
		String value = super.getParameter(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}
	
	/**
	 * 覆蓋getParameterValues方法,將參數名和參數值都做xss過濾
	 */
	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] = xssEncode(values[i]);
		}
		return encodedValues;
	}
	
	/**
	 * 獲取request的屬性時,做xss過濾
	 */
	 @Override
	public Object getAttribute(String name) {
		Object value = super.getAttribute(name);
		if (null != value && value instanceof String) {
			value = xssEncode((String) value);
		}
		return value;
	};
	
	/**
	 * 覆蓋getHeader方法,將參數名和參數值都做xss過濾。<br/>
	 */
	@Override
	public String getHeader(String name) {
		String value = super.getHeader(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}
	
	
	/**
	 * 將容易引起xss漏洞的半角字符直接替換成全角字符
	 * 
	 * @param s
	 * @return
	 */
	private static String xssEncode(String s) {
		if (s == null || s.isEmpty()) {
			return s;
		}
		try {
			HTMLFilter htmlFilter = new HTMLFilter();
			String clean = htmlFilter.filter(s);
			
			return clean;
		} catch (NullPointerException e) {
			return s;
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return null;
	}

	/**
	 * 獲取最原始的request
	 * 
	 * @return
	 */
	public HttpServletRequest getOrgRequest() {
		return orgRequest;
	}

	/**
	 * 獲取最原始的request的靜態方法
	 * 
	 * @return
	 */
	public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
		if (req instanceof XssHttpServletRequestWrapper) {
			return ((XssHttpServletRequestWrapper) req).getOrgRequest();
		}

		return req;
	}
	
}


第四步

        引用 html filter 的實現。採用htmlfitler 開源實現:xss-html-filter


關鍵點

Spring mvc 解析參數時,使用方法:webRequest.getParameterValues(name)獲取參數,因此,request封裝對象只有實現了getParameterValues(String parameter)方法,纔會過濾到controller method 的入參。


Open Declaration Object org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
.resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception
         @Override
	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		MultipartHttpServletRequest multipartRequest =
				WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
		Object arg;

		if (MultipartFile.class.equals(parameter.getParameterType())) {
			assertIsMultipartRequest(servletRequest);
			Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
			arg = multipartRequest.getFile(name);
		}
		else if (isMultipartFileCollection(parameter)) {
			assertIsMultipartRequest(servletRequest);
			Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
			arg = multipartRequest.getFiles(name);
		}
		else if (isMultipartFileArray(parameter)) {
			assertIsMultipartRequest(servletRequest);
			Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
			List<MultipartFile> multipartFiles = multipartRequest.getFiles(name);
			arg = multipartFiles.toArray(new MultipartFile[multipartFiles.size()]);
		}
		else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
			assertIsMultipartRequest(servletRequest);
			arg = servletRequest.getPart(name);
		}
		else if (isPartCollection(parameter)) {
			assertIsMultipartRequest(servletRequest);
			arg = new ArrayList<Object>(servletRequest.getParts());
		}
		else if (isPartArray(parameter)) {
			assertIsMultipartRequest(servletRequest);
			arg = RequestPartResolver.resolvePart(servletRequest);
		}
		else {
			arg = null;
			if (multipartRequest != null) {
				List<MultipartFile> files = multipartRequest.getFiles(name);
				if (!files.isEmpty()) {
					arg = (files.size() == 1 ? files.get(0) : files);
				}
			}
			if (arg == null) {
				String[] paramValues = webRequest.getParameterValues(name);
				if (paramValues != null) {
					arg = paramValues.length == 1 ? paramValues[0] : paramValues;
				}
			}
		}

		return arg;
	}





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