Java安全编码之CSRF

 

此文章为该系列的第二篇。上一篇https://www.anquanke.com/post/id/212897

简介

CSRF攻击者可以利用该漏洞诱使用户执行他们不打算执行的操作。它允许攻击者从不同网站上攻击某一网站的某一用户。使他们执行一些非预期的操作。在这里实验的是我们登陆后,通过CSRF漏洞来修改用户的手机号。

 

框架

此次我们使用SpringBoot作为基础框架,登录框架采用shiro。这里没有具体的dao层

 

配置说明

SpringBoot采用2.3.4.RELEASE版本,Shiro采用1.7.0版本(虽然Shiro时不时的爆出一些漏洞,但是Shiro任然是一款非常优秀的登录框架,使用起来非常的灵活与便捷)。

 

环境搭建

结构如下 ctl为控制层,service为服务层。html文件为Thymeleaf模板,放在templates目录下面。annotation为注解包新增了两个注解AddCSRFToken和CSRFToken。aspect包为切面包。beans包下面包含用户,角色,权限三个bean。config,handler和realm包为一些shiro的配置和拦截过滤器。

 

具体实现

  1. User类
    User类的具体实现如下

    在这里Role分了两种角色,一个是admin一个是user。同时也定义了两种两种权限,query和add。给admin分配query和add权限,给user分配只分配了query权限。
  2. shiro配置
  • Realm实现

    这里实现了两个doGetAuthorizationInfo,其中第一个doGetAuthorizationInfo是我们权限判断,也就是当我们使用subject.hasRole(“admin”),@RequiresRoles(“admin”),<shiro:hashRole name = “admin”>等标签的时候会进入这个判断,主要是判断此用户是否有相应的角色或者权限。第二个doGetAuthorizationInfo是登录的时候进入的逻辑。进入后我们会进行一个Simple的验证。登入成功后会分配授权的sessionID。
  • config配置
    shiro的拦截器配置如下,这里我们吧/loginHtml设置为不需要授权访问的接口,其他都设置为需要授权访问。
  • 接口设定
    设定/query接口为查询手机号码,赋予权限query,/modify接口赋予权限add.
  • csrf测试
    进入http://maoge:8888/loginHtml 后输入账号密码admin/123456,成功进行登录。

    登陆后使用/query查看手机号为13222222222 。

    我们使用burpsuite生成CSRF Poc如下:<html>
    <!– CSRF PoC – generated by Burp Suite Professional –>
    <body>
    <script>history.pushState(‘’, ‘’, ‘/‘)</script>
    <form action=”http://maoge:8888/modify” method=”POST”>
    <input type=”hidden” name=”phone” value=”6666666″ />
    <input type=”submit” value=”Submit request” />
    </form>
    </body>
    </html>

访问如下:

提交后修改成功 手机号也变为6666666

 

修复方式①

这种方式我们通过校验referer的方式来进行判断。但是有一个弊端是并不是所有浏览器在所有情况下都会带上referer。所以这种方式只能作为辅助的验证。这种方式我们可以考虑全局加一个Interceptor。对有referer的情况全都进行判断,但是没有referer的情况下我们放行。

我们把所有的请求都都加入拦截

我们再次请求/modify接口

 

修复方式②

对于第二种方式使用新增csrf token的方式

为了提高灵活性我们新增了两个注解@AddCSRFToken 新增token 和@CSRFToken校验token。使用注解也大大的提高了开发的便利性。

实现的效果如下,当使用:

@AddCSRFToken 注解的实现

这里我们使用线程安全的concurrentHashMap来存储csrfToken,当然也可以使用redis等来进行存储。使用sessionID来作为key,value为一个List。当一个页面出现在多个tab的时候会出现多个csrfToken,这个时候我们使用List存储。@AddCSRFToken 对每个页面添加一个csrfToken的属性,这个token的随机值使用的是owasp里面的生成随机值得方法。

@CSRFToken 注解的实现

先校验接口是是否有csrfToken参数如果有的话就校验,如果此sessionID中存在于concurrentHashMap那么进行判断。否者抛出异常,如果csrfToken为空了,我们可以移除此项。当然当我们sessionID失效的时候,我们也可以全局的移除掉key为此sessionID的项。

CSRFToken也可以放在头部,不过放在自定义头部在请求的时候会发起OPTIONS请求,在后面的CORS章节中会对此进行讨论。

 

后记

当然实现的方式有很多种,开发可以按照自己的方式和思路来进行实现。我们可以使用referer和token的方式做双重校验。当使用json的方式提交数据也会存在CSRF的漏洞,不过大多数依赖于flash,可利用性大大降低。

(完)