spring mvc xss filter

本文详细介绍了在Spring MVC中实现XSS过滤的四个步骤,从配置到关键点,提供了一种有效防止跨站脚本攻击的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
	}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值