CSRF 原理以及 Struts2 令牌校验防御攻略解析

struts2 token 不仅能够有效防止表单重复提交,而且还可以进行 CSRF 验证。
CSRF 攻击原理如下图:

CSRF 原理以及 Struts2 令牌校验防御攻略解析

CSRF 攻击原理图

事实上,B 可能也是一个良性网站,只是被黑客 XSS 劫持了而已。用户实在冤枉啊:我没有上乱七八糟的网站,怎么还是中招了呢?
struts2 token 校验原理如图所示:

Struts2 token 验证原理图

对照 CSRF 攻击原理图,可以看到,虽然 a.jsp 将这个令牌值返回给浏览器,但是 B 是无法拿到这个令牌的具体值的,或者说 B 只能够通过去请求 A 才能拿到一个令牌,但这样就失去了伪造用户请求的目的,因为 session 不一致了。所以 b.action 也就不再处理这个伪造的请求,截断了 CSRF 的流程如下图所示:

CSRF 原理以及 Struts2 令牌校验防御攻略解析

Struts2 token截断了CSRF攻击原理图

了解了原理,那就干活吧。结果发现很多非 form 表单提交的请求都被成功拦截了,比如原有的 ajax 请求、页面的 href 请求、dwr 请求都无法正常工作了——把这些请求的 action 的 token 验证去掉就能正常工作,但却这给黑客带来了可趁之机。
那么怎么样才可以让 form 表单之外的其他请求传递 struts2 token 呢?
我们继续来深入了解一下 struts2 token 验证的原理。

以最新版的 2.3.20 为例。我们在 struts.xml 对某 action 加 标签,事实上是配置了 org.apache.struts2.interceptor.TokenInterceptor 拦截器。查看其 doIntercept 源码:

protected String doIntercept(ActionInvocation invocation) throws Exception { if (log.isDebugEnabled()) { log.debug(Intercepting invocation to check for valid transaction token.); } //see WW-2902: we need to use the real HttpSession here, as opposed to the map //that wraps the session, because a new wrap is created on every request HttpSession session = ServletActionContext.getRequest().getSession(true); synchronized (session) { if (!TokenHelper.validToken()) { return handleInvalidToken(invocation); } } return handleValidToken(invocation); }

它调用了 org.apache.struts2.util.TokenHelper 的 validToken 方法,其源码:

public static boolean validToken() { String tokenName = getTokenName(); if (tokenName == null) { if (LOG.isDebugEnabled()) { LOG.debug(no token name found -> Invalid token ); } return false; } String token = getToken(tokenName); if (token == null) { if (LOG.isDebugEnabled()) { LOG.debug(no token found for token name +tokenName+ -> Invalid token ); } return false; } Map session = ActionContext.getContext().getSession(); String sessionToken = (String) session.get(tokenName); if (!token.equals(sessionToken)) { if (LOG.isWarnEnabled()) { LOG.warn(LocalizedTextUtil.findText(TokenHelper.class, struts.internal.invalid.token, ActionContext.getContext().getLocale(), Form token {0} does not match the session token {1}., new Object[]{token, sessionToken})); } return false; } // remove the token so it won't be used again session.remove(tokenName); return true; }

它先调用自己的 getTokenName() 拿到 tokenName 值,然后根据这个值使用自己的 getToken(tokenName) 拿到客户端请求过来的 token 参数的值,最后跟 session 中保存的值进行比较。
查看 getTokenName() 源码:

public static String getTokenName() { Map params = ActionContext.getContext().getParameters(); if (!params.containsKey(TOKEN_NAME_FIELD)) { if (LOG.isWarnEnabled()) { LOG.warn(Could not find token name in params.); } return null; } String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD); String tokenName; if ((tokenNames == null) || (tokenNames.length < 1)) { if (LOG.isWarnEnabled()) { LOG.warn(Got a null or empty token name.); } return null; } tokenName = tokenNames[0]; return tokenName; }

TOKEN_NAME_FIELD 是 org.apache.struts2.util.TokenHelper 的静态变量,值为 struts.token.name,getTokenName 方法的作用就是拿到客户端传过来的 struts.token.name 参数的值。validToken 拿到这个值以后,根据这个值继续调用 getToken,getToken 源码:

public static String getToken(String tokenName) { if (tokenName == null ) { return null; } Map params = ActionContext.getContext().getParameters(); String[] tokens = (String[]) params.get(tokenName); String token; if ((tokens == null) || (tokens.length < 1)) { if (LOG.isWarnEnabled()) { LOG.warn(Could not find token mapped to token name + tokenName); } return null; } token = tokens[0]; return token; }

它根据 struts.token.name 参数的值,比如为 token(这个值可以在 jsp 的 token 标签中进行配置,比如 ,默认值是 token),拿到客户端传来的 token 参数的值,最后返回给 validToken 方法。
使用 chrome 的开发者工具截取到的带有 token 验证的 struts action 印证了上面的说法:

CSRF 原理以及 Struts2 令牌校验防御攻略解析

截取到的struts.token.name的值

原理都了解了,接下来的事情似乎就顺理成章了。
$.ajax 调用支持 struts2 token:

<script> var strutsToken = ; var token = { struts.token.name: token, token: strutsToken }; $.ajax({ url: '/endpoint', data: token, dataType: 'jsonp', cache: true, success: function() { console.log('success'); }, error: function() { console.log('failure'); } }); </script>

页面 href 请求支持 struts2 token:

title=Edit Meta>查看详细

分类:默认分类 时间:2012-01-03 人气:7
本文关键词:
分享到:

相关文章

  • Flask使用token来防御csrf跨站攻击 2012-01-14

    做pytoner工程师很多时候也是需要写web的。 那么你已经会碰到被攻击的危险。比如Csrf攻击,那么csrf是什么我这里就不再详细的描述,我想大家也应该知道。 Flask本身提供了插件,一些社区也在简单的token之上又做了一些关于csrf的防御。 图中Browse是浏览器,WebServerA是受信任网站/被攻击网站A,WebServerB是恶意网站/攻击网站B。 (1),一开始用户打开浏览器,访问受信任网站A,输入用户名和密码登陆请求登陆网站A。 (2),网站A验证用户信息,用户信息通过

  • 那些年,那些 Apache Struts2 的漏洞 2012-01-15

    每次 Apache Struts2 漏洞爆发都在互联网上掀起腥风血雨,我们整理了近年来 Apache Struts2 高风险漏洞的信息供大家参考。针对此次 Apache Struts2 的漏洞,绿盟科技已提供在线检查工具帮助大家自查此漏洞。 1. Apache Struts2 漏洞的背景 2. OGNL简介 ​

  • Struts2的Action中取得Session.Request.Application对象的方法 2012-01-23

    Struts2的Action中取得Session、Request、Application对象的方法最常用的方法是: private Map session; private Map request; private Map application; session = ActionContext.getContext().getSession(); request = (Map) ActionContext.getContext().get("request"); application = A

  • Struts1.Struts2和SpringMVC剖析 2012-01-24

    前段框架用了不少,今天就来做个总结。网上关于Struts1、Struts2、SpringMVC的文章有很多,这里的内容就是基于它们,来做个比较。 这三个框架是按照上面的顺序,依次出现的,它们都是对MVC模式的实现。为什么会出现这三个、甚至更多的MVC框架呢?他们都是为了将URL世界映射到Java世界。尽管它们它们内部的实现思路不同,有着各自的优缺点,但是它们都做到了个自己的使用目的。 历史介绍 大多Web应用程序,都是运行在HTTP上的。HTTP协议是一系列无状态的文本传输协议。无状态的协议不记

  • phpok csrf成功getshell(二) 2012-02-11

    前台注册一个帐号,上传zip文件,csrf后台升级(升级文件为上传的zip),成功getshell 对升级文件没进行校验。 版本:4.2.100 前台能上传zip文件,而且对升级文件没进行校验。 上传zip演示: 先把我们的木马文件test.php添加到压缩包内test.zip 注册一个帐号-修改资料. 选择一个正常图片,截获数据 然后修改数据。 成功上传zip文件。记录下文件id号。我们这里是739 在 程序升级 ? ZIP离线包升级 中的升级操作没有进行Referer验证。导致csrf产生

  • Struts2与Weblogic服务器配置注意事项 2012-02-23

    最近新项目打算用struts2与weblogic服务器搭建项目环境,本以为会很容易成功,没想到却花了我好多时间。 进入正题: 环境:weblogic9.2 MP3 、JDK1.7.3、Myeclipse8.5 一开始按照基本的struts2搭配环节进行,发现在服务器启动的时候,报错,大致如下: <<WLS Kernel>> <> <> <1272511848921> <BEA-101165> <Could not lo

  • dhtmlxTree+struts2实现简单的动态树形菜单 2012-02-27

    页面 "> My JSP 'index.jsp' starting page <script type="text/javascript" src="jquery-1.10.2.js"></script> <script type="text/javascript" src="dhtmlxcommon.js"></script> <script type="text/javascript" src="dhtmlxtree.js">

  • Discuz!全版本鸡肋CSRF一枚 2012-03-04

    没想到Discuz还存在csrf额,测试的时候发现基本上请求都有带token之类的防止csrf的参数,但是还是被我找到了一处,吼吼。通用型有奖励! 全版本通杀!~ 不得不说dz的csrf几乎么有,这个csrf是收藏论坛板块的功能。 直接get请求的csrf,下面是我本地搭建测试的。 url为 http://www.xxx.com/home.php?mod=spacecp&ac=favorite&type=forum&id=2&handlekey=favoritef

  • 构造XSS-CSRF代码+Discuz添加用户EXP 2012-03-09

    最近在研究各类XSS,自然少不了要构造各种CSRF. 构造这玩意其实很简单本地搭个环境 管理员登陆后对指定操作进行抓包,然后JS或FLASH来XXOO 转换功能很简单,但非常实用,XSS流行的年代,这功能也将成为常用不仅这样还省掉N多时间 post参数可能很多,手工构造不但费时且遗漏其中一个参数都有可能导致CSRF失败. 我问你怕了吗? 图片: 功能: [+]EncodeDecode Post提交代码(Flash/jQuery/Flash/Python/Perl/PHP) 20141208 [+

Copyright (C) quwantang.com, All Rights Reserved.

趣玩堂 版权所有 京ICP备15002868号

processed in 0.076 (s). 10 q(s)