标签:项目 nal 上下 oid print content enabled comm work
XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
spring + struts2 +.....(仅列举相关的)
防止xss攻击
1.防止xss攻击,可以从请求处拦截特殊字符,核心是过滤特殊字符串
2.由于项目是采用struts2来处理请求的,所以应从struts处着手找方案
3.struts2中的StrutsPrepareAndExecuteFilter,核心是一个Filter,Action可以脱离web容器,让http请求和action关联在一起的
4.可以继承StrutsPrepareAndExecuteFilter类来实现特殊字符过滤
StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,Filter方法调用顺序是init—>doFilter—>destroy
注意:只关心怎么实现可以跳过这个内容,直接看下面实现部分
1.init方法
init是Filter第一个运行的方法,主要的工作是初始化一些配置
public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 FilterHostConfig config = new FilterHostConfig(filterConfig); // 初始化struts内部日志 init.initLogging(config); //创建dispatcher ,加载资源 dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config, dispatcher); //初始化类属性:prepare 、execute this.prepare = new PrepareOperations(dispatcher); this.execute = new ExecuteOperations(dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); //回调空的postInit方法 this.postInit(dispatcher, filterConfig); } finally { if (dispatcher != null) { dispatcher.cleanUpAfterInit(); } init.cleanup(); } }
1-1.FilterHostConfig类
可以看出,FilterHostConfig主要是对配置文件的一个封装,如果有需求要操作配置文件的参数,可以继承此类实现我们的业务
public class FilterHostConfig implements HostConfig { private FilterConfig config; /** *构造函数 */ public FilterHostConfig(FilterConfig config) { this.config = config; } /** * 根据init-param配置的param-name获取param-value的值 */ public String getInitParameter(String key) { return this.config.getInitParameter(key); } /** * 返回初始化参数名的List */ public Iterator<String> getInitParameterNames() { return MakeIterator.convert(this.config.getInitParameterNames()); } /** * 返回上下文 */ public ServletContext getServletContext() { return this.config.getServletContext(); } }
1-2.初始化Dispatcher
创建Dispatcher,会读取 filterConfig 中的配置信息
public Dispatcher initDispatcher(HostConfig filterConfig) { Dispatcher dispatcher = this.createDispatcher(filterConfig); dispatcher.init(); return dispatcher;
}
Dispatcher类init的源码
public void init() { if (this.configurationManager == null) { this.configurationManager = this .createConfigurationManager("struts"); } try { this.init_FileManager(); //加载org/apache/struts2/default.properties this.init_DefaultProperties(); //加载struts-default.xml,struts-plugin.xml,struts.xml this.init_TraditionalXmlConfigurations(); this.init_LegacyStrutsProperties(); //用户自己实现的ConfigurationProviders类 this.init_CustomConfigurationProviders(); //Filter的初始化参数 this.init_FilterInitParameters(); this.init_AliasStandardObjects(); Container ex = this.init_PreloadConfiguration(); ex.inject(this); this.init_CheckWebLogicWorkaround(ex); if (!dispatcherListeners.isEmpty()) { Iterator i$ = dispatcherListeners.iterator(); while (i$.hasNext()) { DispatcherListener l = (DispatcherListener) i$.next(); l.dispatcherInitialized(this); } } this.errorHandler.init(this.servletContext); } catch (Exception arg3) { if (LOG.isErrorEnabled()) { LOG.error("Dispatcher initialization failed", arg3, new String[0]); } throw new StrutsException(arg3); } }
1-3.创建Dispatcher
将配置信息解析出来,封装成为一个Map,然后根据servlet上下文和参数Map构造Dispatcher
private Dispatcher createDispatcher(HostConfig filterConfig) { HashMap params = new HashMap(); Iterator e = filterConfig.getInitParameterNames(); while (e.hasNext()) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); } return new Dispatcher(filterConfig.getServletContext(), params); }
2.doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //父类向子类转:强转为http请求、响应 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) { chain.doFilter(request, response); } else { //设置编码和国际化 this.prepare.setEncodingAndLocale(request, response); //创建Action上下文 this.prepare.createActionContext(request, response); this.prepare.assignDispatcherToThread(); request = this.prepare.wrapRequest(request); ActionMapping mapping = this.prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = this.execute .executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { this.execute.executeAction(request, response, mapping); } } } finally { this.prepare.cleanupRequest(request); } }
2-1.setEncodingAndLocale
设置编码这是调用了Dispatcherde.prepare方法加载配置
public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { this.dispatcher.prepare(request, response); }
prepare方法只是做一些初始化和国际化的缺省设置
public void prepare(HttpServletRequest request, HttpServletResponse response) { String encoding = null; if (this.defaultEncoding != null) { encoding = this.defaultEncoding; } if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { encoding = "UTF-8"; } Locale locale = null; if (this.defaultLocale != null) { locale = LocalizedTextUtil.localeFromString(this.defaultLocale, request.getLocale()); } if (encoding != null) { this.applyEncoding(request, encoding); } if (locale != null) { response.setLocale(locale); } if (this.paramsWorkaroundEnabled) { request.getParameter("foo"); } }
2-2.createActionContext
ActionContext是一个容器,这个容易主要存储request、session、application、parameters等
ActionContext是一个线程的本地变量,不同的action之间不会共享ActionContext,所以也不用考虑线程安全问题。
实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象
private static ThreadLocal<Dispatcher> instance = new ThreadLocal(); protected Map<String, String> initParams;
创建Action上下文,初始化thread local
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { Integer counter = Integer.valueOf(1); Integer oldCounter = (Integer) request .getAttribute("__cleanup_recursion_counter"); if (oldCounter != null) { counter = Integer.valueOf(oldCounter.intValue() + 1); } //从ThreadLocal中获取此ActionContext变量 ActionContext oldContext = ActionContext.getContext(); ActionContext ctx; if (oldContext != null) { ctx = new ActionContext(new HashMap(oldContext.getContextMap())); } else { ValueStack stack = ((ValueStackFactory) this.dispatcher .getContainer().getInstance(ValueStackFactory.class)) .createValueStack(); stack.getContext().putAll( this.dispatcher.createContextMap(request, response, (ActionMapping) null)); //stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext ctx = new ActionContext(stack.getContext()); } request.setAttribute("__cleanup_recursion_counter", counter); //将ActionContext存到ThreadLocal<ActionContext> actionContext ActionContext.setContext(ctx); return ctx; }
createContextMap方法
public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { RequestMap requestMap = new RequestMap(request); HashMap params = new HashMap(request.getParameterMap()); SessionMap session = new SessionMap(request); ApplicationMap application = new ApplicationMap(this.servletContext); //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). HashMap extraContext = this.createContextMap(requestMap, params, session, application, request, response); if (mapping != null) { extraContext.put("struts.actionMapping", mapping); } return extraContext; }
executeAction方法
封装执行的上下文环境,主要将相关信息存储入map,根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象,执行execute方法,并转向结果
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { this.dispatcher.serviceAction(request, response, mapping); }
继承StrutsPrepareAndExecuteFilter类,重写init方法,在web.xml引用这个filter
@Override public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { FilterHostConfig config = new FilterHostConfig(filterConfig); init.initLogging(config); dispatcher = initDispatcher(config); init.initStaticContentLoader(config, dispatcher); prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig); } finally { if (dispatcher != null) { dispatcher.cleanUpAfterInit(); } init.cleanup(); } } public Dispatcher initDispatcher(HostConfig filterConfig) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; } private Dispatcher createDispatcher(HostConfig filterConfig) { Map<String, String> params = new HashMap<String, String>(); for (Iterator e = filterConfig.getInitParameterNames(); e.hasNext();) { String name = (String) e.next(); String value = filterConfig.getInitParameter(name); params.put(name, value); } return new MDispatcher(filterConfig.getServletContext(), params); }
继承Dispatcher实现自己的MDispatcher重写createContextMap方法
@Override public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { // request map wrapping the http request objects Map requestMap = new RequestMap(request); //对参数进行xss过滤 Map<String, String[]> paramMap = xssFilterParam(request); // parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separately Map params = new HashMap(paramMap); // session map wrapping the http session Map session = new SessionMap(request); // application map wrapping the ServletContext Map application = new ApplicationMap(servletContext); Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response); if (mapping != null) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; }
xssFilterParam
private Map<String, String[]> xssFilterParam(HttpServletRequest request) { Map<String, String[]> paramMap = new HashMap<String, String[]>(); Map<String, String[]> reqParamMap = request.getParameterMap(); for (Map.Entry<String, String[]> entry : reqParamMap.entrySet()) { String key = entry.getKey(); LOG.debug("Key = " + entry.getKey() + ", Value = " + entry.getValue()); if(!key.endsWith("$HTML")){ //过滤 String[] values = getParameterValues(request,key); paramMap.put(key, values); }else{ //如果以 “$HTML” 结尾的参数,不进行过滤,并且构造新参数值 String keyNew = key.substring(0,key.length()-5); paramMap.put(keyNew, entry.getValue()); paramMap.put(key, entry.getValue()); } } return paramMap; }
getParameterValues
private String[] getParameterValues(HttpServletRequest request,String name) { name = XssShieldUtil.stripXss(name); // 返回值之前 先进行过滤 String[] values = request.getParameterValues(name); if(values != null){ for (int i = 0; i < values.length; i++) { values[i] = XssShieldUtil.stripXss(values[i]); } } return values; }
我的xss字符处理工具类
public class XssShieldUtil { private static List<Pattern> patterns = null; private static List<Object[]> getXssPatternList() { List<Object[]> ret = new ArrayList<Object[]>(); ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE}); ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL}); ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL}); ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE}); ret.add(new Object[]{"<(\"[^\"]*\"|\‘[^\‘]*\‘|[^\‘\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL}); ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL}); ret.add(new Object[]{"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL}); return ret; } private static List<Pattern> getPatterns() { if (patterns == null) { List<Pattern> list = new ArrayList<Pattern>(); String regex = null; Integer flag = null; int arrLength = 0; for(Object[] arr : getXssPatternList()) { arrLength = arr.length; for(int i = 0; i < arrLength; i++) { regex = (String)arr[0]; flag = (Integer)arr[1]; list.add(Pattern.compile(regex, flag)); } } patterns = list; } return patterns; } public static String stripXss(String value) { if(StringUtils.isNotBlank(value)) { Matcher matcher = null; for(Pattern pattern : getPatterns()) { matcher = pattern.matcher(value); // 匹配 if(matcher.find()) { // 删除相关字符串 value = matcher.replaceAll(""); } } value = value.replaceAll("<", "<").replaceAll(">", ">"); } return value; } }
最后在web.xml引用这个StrutsPrepareAndExecuteFilter
<filter> <filter-name>struts2</filter-name> <filter-class>MStrutsPrepareAndExecuteFilter</filter-class> </filter>
标签:项目 nal 上下 oid print content enabled comm work
原文地址:http://www.cnblogs.com/maixiaodou/p/7372007.html