概念

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分析

StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,Filter方法调用顺序是init—>doFilter—>destroy

注意:只关心怎么实现可以跳过这个内容,直接看下面实现部分

1.init方法

   init是Filter第一个运行的方法,主要的工作是初始化一些配置

  1. public void init(FilterConfig filterConfig) throws ServletException {
  2. InitOperations init = new InitOperations();
  3. Dispatcher dispatcher = null;
  4. try {
  5. //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
  6. FilterHostConfig config = new FilterHostConfig(filterConfig);
  7. // 初始化struts内部日志
  8. init.initLogging(config);
  9. //创建dispatcher ,加载资源
  10. dispatcher = init.initDispatcher(config);
  11. init.initStaticContentLoader(config, dispatcher);
  12. //初始化类属性:prepare 、execute
  13. this.prepare = new PrepareOperations(dispatcher);
  14. this.execute = new ExecuteOperations(dispatcher);
  15. this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  16. //回调空的postInit方法
  17. this.postInit(dispatcher, filterConfig);
  18. } finally {
  19. if (dispatcher != null) {
  20. dispatcher.cleanUpAfterInit();
  21. }
  22. init.cleanup();
  23. }
  24. }

1-1.FilterHostConfig类

  可以看出,FilterHostConfig主要是对配置文件的一个封装,如果有需求要操作配置文件的参数,可以继承此类实现我们的业务

  1. public class FilterHostConfig implements HostConfig {
  2. private FilterConfig config;
  3. /**
  4. *构造函数
  5. */
  6. public FilterHostConfig(FilterConfig config) {
  7. this.config = config;
  8. }
  9. /**
  10. * 根据init-param配置的param-name获取param-value的值
  11. */
  12. public String getInitParameter(String key) {
  13. return this.config.getInitParameter(key);
  14. }
  15. /**
  16. * 返回初始化参数名的List
  17. */
  18. public Iterator<String> getInitParameterNames() {
  19. return MakeIterator.convert(this.config.getInitParameterNames());
  20. }
  21. /**
  22. * 返回上下文
  23. */
  24. public ServletContext getServletContext() {
  25. return this.config.getServletContext();
  26. }
  27. }

1-2.初始化Dispatcher

    创建Dispatcher,会读取 filterConfig 中的配置信息

  1. public Dispatcher initDispatcher(HostConfig filterConfig) {
  2. Dispatcher dispatcher = this.createDispatcher(filterConfig);
  3. dispatcher.init();
  4. return dispatcher;
    }

Dispatcher类init的源码

  1. public void init() {
  2. if (this.configurationManager == null) {
  3. this.configurationManager = this
  4. .createConfigurationManager("struts");
  5. }
  6.  
  7. try {
  8.  
  9. this.init_FileManager();
  10. //加载org/apache/struts2/default.properties
  11. this.init_DefaultProperties();
  12. //加载struts-default.xml,struts-plugin.xml,struts.xml
  13. this.init_TraditionalXmlConfigurations();
  14. this.init_LegacyStrutsProperties();
  15. //用户自己实现的ConfigurationProviders类
  16. this.init_CustomConfigurationProviders();
  17. //Filter的初始化参数
  18. this.init_FilterInitParameters();
  19. this.init_AliasStandardObjects();
  20. Container ex = this.init_PreloadConfiguration();
  21. ex.inject(this);
  22. this.init_CheckWebLogicWorkaround(ex);
  23. if (!dispatcherListeners.isEmpty()) {
  24. Iterator i$ = dispatcherListeners.iterator();
  25.  
  26. while (i$.hasNext()) {
  27. DispatcherListener l = (DispatcherListener) i$.next();
  28. l.dispatcherInitialized(this);
  29. }
  30. }
  31.  
  32. this.errorHandler.init(this.servletContext);
  33. } catch (Exception arg3) {
  34. if (LOG.isErrorEnabled()) {
  35. LOG.error("Dispatcher initialization failed", arg3,
  36. new String[0]);
  37. }
  38.  
  39. throw new StrutsException(arg3);
  40. }
  41. }

1-3.创建Dispatcher

  将配置信息解析出来,封装成为一个Map,然后根据servlet上下文和参数Map构造Dispatcher

  1. private Dispatcher createDispatcher(HostConfig filterConfig) {
  2. HashMap params = new HashMap();
  3. Iterator e = filterConfig.getInitParameterNames();
  4. while (e.hasNext()) {
  5. String name = (String) e.next();
  6. String value = filterConfig.getInitParameter(name);
  7. params.put(name, value);
  8. }
  9. return new Dispatcher(filterConfig.getServletContext(), params);
  10. }

2.doFilter方法

  1. public void doFilter(ServletRequest req, ServletResponse res,
  2. FilterChain chain) throws IOException, ServletException {
  3. //父类向子类转:强转为http请求、响应
  4. HttpServletRequest request = (HttpServletRequest) req;
  5. HttpServletResponse response = (HttpServletResponse) res;
  6. try {
  7. if (this.excludedPatterns != null
  8. && this.prepare.isUrlExcluded(request,
  9. this.excludedPatterns)) {
  10. chain.doFilter(request, response);
  11. } else {
  12. //设置编码和国际化
  13. this.prepare.setEncodingAndLocale(request, response);
  14. //创建Action上下文
  15. this.prepare.createActionContext(request, response);
  16. this.prepare.assignDispatcherToThread();
  17. request = this.prepare.wrapRequest(request);
  18. ActionMapping mapping = this.prepare.findActionMapping(request,
  19. response, true);
  20. if (mapping == null) {
  21. boolean handled = this.execute
  22. .executeStaticResourceRequest(request, response);
  23. if (!handled) {
  24. chain.doFilter(request, response);
  25. }
  26. } else {
  27. this.execute.executeAction(request, response, mapping);
  28. }
  29. }
  30. } finally {
  31. this.prepare.cleanupRequest(request);
  32. }
  33.  
  34. }

2-1.setEncodingAndLocale

  设置编码这是调用了Dispatcherde.prepare方法加载配置

  1. public void setEncodingAndLocale(HttpServletRequest request,
  2. HttpServletResponse response) {
  3. this.dispatcher.prepare(request, response);
  4. }

prepare方法只是做一些初始化和国际化的缺省设置

  1. public void prepare(HttpServletRequest request, HttpServletResponse response) {
  2. String encoding = null;
  3. if (this.defaultEncoding != null) {
  4. encoding = this.defaultEncoding;
  5. }
  6.  
  7. if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
  8. encoding = "UTF-8";
  9. }
  10.  
  11. Locale locale = null;
  12. if (this.defaultLocale != null) {
  13. locale = LocalizedTextUtil.localeFromString(this.defaultLocale,
  14. request.getLocale());
  15. }
  16.  
  17. if (encoding != null) {
  18. this.applyEncoding(request, encoding);
  19. }
  20.  
  21. if (locale != null) {
  22. response.setLocale(locale);
  23. }
  24.  
  25. if (this.paramsWorkaroundEnabled) {
  26. request.getParameter("foo");
  27. }
  28.  
  29. }

2-2.createActionContext

   ActionContext是一个容器,这个容易主要存储request、session、application、parameters等

ActionContext是一个线程的本地变量,不同的action之间不会共享ActionContext,所以也不用考虑线程安全问题。

实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象

  1. private static ThreadLocal<Dispatcher> instance = new ThreadLocal();
  2. protected Map<String, String> initParams;

创建Action上下文,初始化thread local

  1. public ActionContext createActionContext(HttpServletRequest request,
  2. HttpServletResponse response) {
  3. Integer counter = Integer.valueOf(1);
  4. Integer oldCounter = (Integer) request
  5. .getAttribute("__cleanup_recursion_counter");
  6. if (oldCounter != null) {
  7. counter = Integer.valueOf(oldCounter.intValue() + 1);
  8. }
  9. //从ThreadLocal中获取此ActionContext变量
  10. ActionContext oldContext = ActionContext.getContext();
  11. ActionContext ctx;
  12. if (oldContext != null) {
  13. ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
  14. } else {
  15. ValueStack stack = ((ValueStackFactory) this.dispatcher
  16. .getContainer().getInstance(ValueStackFactory.class))
  17. .createValueStack();
  18. stack.getContext().putAll(
  19. this.dispatcher.createContextMap(request, response,
  20. (ActionMapping) null));
  21. //stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext
  22. ctx = new ActionContext(stack.getContext());
  23. }
  24.  
  25. request.setAttribute("__cleanup_recursion_counter", counter);
  26. //将ActionContext存到ThreadLocal<ActionContext> actionContext
  27. ActionContext.setContext(ctx);
  28. return ctx;
  29. }

createContextMap方法

  1. public Map<String, Object> createContextMap(HttpServletRequest request,
  2. HttpServletResponse response, ActionMapping mapping) {
  3. RequestMap requestMap = new RequestMap(request);
  4. HashMap params = new HashMap(request.getParameterMap());
  5. SessionMap session = new SessionMap(request);
  6. ApplicationMap application = new ApplicationMap(this.servletContext);
  7. //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p).
  8. HashMap extraContext = this.createContextMap(requestMap, params,
  9. session, application, request, response);
  10. if (mapping != null) {
  11. extraContext.put("struts.actionMapping", mapping);
  12. }
  13. return extraContext;
  14. }

executeAction方法

  封装执行的上下文环境,主要将相关信息存储入map,根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象,执行execute方法,并转向结果

  1. public void executeAction(HttpServletRequest request,
  2. HttpServletResponse response, ActionMapping mapping)
  3. throws ServletException {
  4. this.dispatcher.serviceAction(request, response, mapping);
  5. }

  看到这,会有一种不知所措的感觉,毕竟源码都是毕竟乏味的,我整理了张关系图如下,后面我们要实现的就是重写StrutsPrepareAndExecuteFilter来管理

重写并添加了自己规则的Dispatcher,也就是执行者,这里,我举个现实中关系的例子:

FilterHostConfig:开发的规范
Dispatcher:程序员
InitOperations:项目经理
StrutsPrepareAndExecuteFilter:老板

程序员要严格按照规范来写代码,项目经理要负责跟进程序员的整个工作过程,项目经理则只需对老板负责,定期汇报

上面的源码就是我按照这种关系分析的,这样比较好理解,关系就一目了然

(提示:如果图片看不清,可以右击保存到桌面查看)

执行者管理器的流程是这样的,后面实现部分就是着重修改这里,添加字符的拦截规则

(提示:如果图片看不清,可以右击保存到桌面查看)

实现

继承StrutsPrepareAndExecuteFilter类,重写init方法,在web.xml引用这个filter

  1. @Override
  2. public void init(FilterConfig filterConfig) throws ServletException {
  3. InitOperations init = new InitOperations();
  4. Dispatcher dispatcher = null;
  5. try {
  6. FilterHostConfig config = new FilterHostConfig(filterConfig);
  7. init.initLogging(config);
  8. dispatcher = initDispatcher(config);
  9. init.initStaticContentLoader(config, dispatcher);
  10.  
  11. prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
  12. execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
  13. this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  14.  
  15. postInit(dispatcher, filterConfig);
  16. } finally {
  17. if (dispatcher != null) {
  18. dispatcher.cleanUpAfterInit();
  19. }
  20. init.cleanup();
  21. }
  22. }
  23.  
  24. public Dispatcher initDispatcher(HostConfig filterConfig) {
  25. Dispatcher dispatcher = createDispatcher(filterConfig);
  26. dispatcher.init();
  27. return dispatcher;
  28. }
  29.  
  30. private Dispatcher createDispatcher(HostConfig filterConfig) {
  31. Map<String, String> params = new HashMap<String, String>();
  32. for (Iterator e = filterConfig.getInitParameterNames(); e.hasNext();) {
  33. String name = (String) e.next();
  34. String value = filterConfig.getInitParameter(name);
  35. params.put(name, value);
  36. }
  37. return new MDispatcher(filterConfig.getServletContext(), params);
  38. }

继承Dispatcher实现自己的MDispatcher重写createContextMap方法

  1. @Override
  2. public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
  3. ActionMapping mapping) {
  4.  
  5. // request map wrapping the http request objects
  6. Map requestMap = new RequestMap(request);
  7.  
  8. //对参数进行xss过滤
  9. Map<String, String[]> paramMap = xssFilterParam(request);
  10.  
  11. // parameters map wrapping the http parameters. ActionMapping parameters are now handled and applied separately
  12. Map params = new HashMap(paramMap);
  13.  
  14. // session map wrapping the http session
  15. Map session = new SessionMap(request);
  16.  
  17. // application map wrapping the ServletContext
  18. Map application = new ApplicationMap(servletContext);
  19.  
  20. Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);
  21.  
  22. if (mapping != null) {
  23. extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
  24. }
  25. return extraContext;
  26. }

xssFilterParam

  1. private Map<String, String[]> xssFilterParam(HttpServletRequest request) {
  2. Map<String, String[]> paramMap = new HashMap<String, String[]>();
  3. Map<String, String[]> reqParamMap = request.getParameterMap();
  4. for (Map.Entry<String, String[]> entry : reqParamMap.entrySet()) {
  5. String key = entry.getKey();
  6. LOG.debug("Key = " + entry.getKey() + ", Value = " + entry.getValue());
  7. if(!key.endsWith("$HTML")){
  8. //过滤
  9. String[] values = getParameterValues(request,key);
  10. paramMap.put(key, values);
  11. }else{
  12. //如果以 “$HTML” 结尾的参数,不进行过滤,并且构造新参数值
  13. String keyNew = key.substring(0,key.length()-5);
  14. paramMap.put(keyNew, entry.getValue());
  15. paramMap.put(key, entry.getValue());
  16. }
  17.  
  18. }
  19. return paramMap;
  20. }

getParameterValues

  1. private String[] getParameterValues(HttpServletRequest request,String name) {
  2. name = XssShieldUtil.stripXss(name);
  3. // 返回值之前 先进行过滤
  4. String[] values = request.getParameterValues(name);
  5. if(values != null){
  6. for (int i = 0; i < values.length; i++) {
  7. values[i] = XssShieldUtil.stripXss(values[i]);
  8. }
  9. }
  10. return values;
  11. }

我的xss字符处理工具类

  1. public class XssShieldUtil {
  2.  
  3. private static List<Pattern> patterns = null;
  4.  
  5. private static List<Object[]> getXssPatternList() {
  6. List<Object[]> ret = new ArrayList<Object[]>();
  7. ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
  8. ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
  9. ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
  10. ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
  11. ret.add(new Object[]{"<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
  12. ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
  13. 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});
  14. return ret;
  15. }
  16.  
  17. private static List<Pattern> getPatterns() {
  18.  
  19. if (patterns == null) {
  20.  
  21. List<Pattern> list = new ArrayList<Pattern>();
  22.  
  23. String regex = null;
  24. Integer flag = null;
  25. int arrLength = 0;
  26.  
  27. for(Object[] arr : getXssPatternList()) {
  28. arrLength = arr.length;
  29. for(int i = 0; i < arrLength; i++) {
  30. regex = (String)arr[0];
  31. flag = (Integer)arr[1];
  32. list.add(Pattern.compile(regex, flag));
  33. }
  34. }
  35.  
  36. patterns = list;
  37. }
  38.  
  39. return patterns;
  40. }
  41.  
  42. public static String stripXss(String value) {
  43. if(StringUtils.isNotBlank(value)) {
  44.  
  45. Matcher matcher = null;
  46.  
  47. for(Pattern pattern : getPatterns()) {
  48. matcher = pattern.matcher(value);
  49. // 匹配
  50. if(matcher.find()) {
  51. // 删除相关字符串
  52. value = matcher.replaceAll("");
  53. }
  54. }
  55.  
  56. value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
  57. }
  58. return value;
  59. }
  60. }

最后在web.xml引用这个StrutsPrepareAndExecuteFilter

  1. <filter>
  2. <filter-name>struts2</filter-name>
  3. <filter-class>MStrutsPrepareAndExecuteFilter</filter-class>
  4. </filter>

特殊字符的过滤,防止xss攻击的更多相关文章

  1. PHP不过过滤防止xss攻击的方法

    PHP不过过滤防止xss攻击的方法<pre> $content=htmlspecialchars($content); $pos=strpos($content,"\u" ...

  2. 拦截过滤防御XSS攻击 -- Struts2.3 以及 2.5 的解决方式

    使用Struts2框架开发的后台在防御XSS攻击的时候很多方式都不能用,因为Struts2对请求进行的二次封装有区别.以下针对Struts2的XSS攻击进行拦截过滤防御解决: Struts2.3 本方 ...

  3. 变量安全过滤,防止xss攻击

    下面这个方法不管是字符串还是数组,都可以进行过滤 /** * @purpose : 对变量进行安全过滤,使 $_GET.$_POST.$q->record 等变量更安全 * @author : ...

  4. PHP通用的XSS攻击过滤函数,Discuz系统中 防止XSS漏洞攻击,过滤HTML危险标签属性的PHP函数

    XSS攻击在最近很是流行,往往在某段代码里一不小心就会被人放上XSS攻击的代码,看到国外有人写上了函数,咱也偷偷懒,悄悄的贴上来... 原文如下: The goal of this function ...

  5. 文本XSS攻击过滤

    在FCK或百度编辑器等常用富文本编辑器中,通常是会被XSS攻击 处理方法: 文本框模拟输入了以下文本 <span style="dispaly:none" onclick=& ...

  6. 前端过滤XSS攻击

    日常开发过程中,对于存在用户交互的一些门户网站等,过滤xss攻击是必不可少的. 此处主要记录下我在工作过程中的简单处理方法. 前端过滤XSS攻击, 我这里用的是开源工程 js-xss,官网地址:htt ...

  7. XSS攻击过滤处理

    关于XSS攻击 XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中. XSS漏洞的危害 网络钓鱼,包括盗取各类用户账号: 窃取用户cooki ...

  8. 根据白名单过滤 HTML(防止 XSS 攻击)

    https://github.com/leizongmin/js-xss/blob/master/README.zh.md 根据白名单过滤 HTML(防止 XSS 攻击) xss是一个用于对用户输入的 ...

  9. # 防止xss攻击,过滤script标签,获取出标签外的内容

    from bs4 import BeautifulSoups = '<h1>123</h1> <span>456<span>'soup = Beauti ...

随机推荐

  1. java的AES对称加密和解密,有偏移量

    import java.math.BigDecimal; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; i ...

  2. Python练习二

    1.计算 1 - 2 + 3 ... + 99 中除了88以外所有数的总和 sum1 = 0 sum2 = 0 count = 0 while count < 99: count += 1 if ...

  3. 学习笔记CB010:递归神经网络、LSTM、自动抓取字幕

    递归神经网络可存储记忆神经网络,LSTM是其中一种,在NLP领域应用效果不错. 递归神经网络(RNN),时间递归神经网络(recurrent neural network),结构递归神经网络(recu ...

  4. vue----计算与监听属性

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name=& ...

  5. Linux 开(关) ICMP 回应(防止被ping)

    临时生效的办法 关闭回应: [root@host ~]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all // 客户端测试 ➜ ~ ping 0 ...

  6. linux子系统ubuntu16.04安装使用xrdp当远程桌面

    参考文献:https://icytown.com/windows/windows-subsystem-for-linux-gui-xubuntu/ https://jingyan.baidu.com/ ...

  7. The Oregon Trail 俄勒冈之旅

    发售年份 1971 平台 多平台 开发商 MECC 类型 教育娱乐 https://www.youtube.com/watch?v=QBOLN7I8omY

  8. layui 表格组件不能访问连续的属性的解决办法

    table.js里第741行改成这样,它这里只能访问一级属性// var content = item1[field]; 不能访问对象内的对象,比如item1.depart.name,只能访问一级属性 ...

  9. java8_api_jdbc

    jdbc-1    jdbc的概念    驱动的分类    连接oracle数据库        与任何表格数据源交互        代码编写步骤        加载驱动            Cla ...

  10. 密码疑云 (3)——详解RSA的加密与解密

    上一篇文章介绍了RSA涉及的数学知识,本章将应用这些知识详解RSA的加密与解密. RSA算法的密钥生成过程 密钥的生成是RSA算法的核心,它的密钥对生成过程如下: 1. 选择两个不相等的大素数p和q, ...