SpringBoot 如何防御 CSRF 攻击?

番茄系统家园 · 2022-03-22 07:18:55

SpringBoot 如何防御 CSRF 攻击?

CSRF 就是跨域请求伪造,英文全称是 Cross Site Request Forgery。

这是一种非常常见的 Web 攻击方式,其实是很好防御的,但是由于经常被很多开发者忽略,进而导致很多网站实际上都存在 CSRF 攻击的安全隐患。

今天松哥就来和大家聊一聊什么是 CSRF 攻击以及 CSRF 攻击该如何防御。

1.CSRF原理

想要防御 CSRF 攻击,那我们得先搞清楚什么是 CSRF 攻击,松哥通过下面一张图,来和大家梳理 CSRF 攻击流程:

SpringBoot 如何防御 CSRF 攻击?

其实这个流程很简单:

  • 假设用户打开了招商银行网上银行网站,并且登录。
  • 登录成功后,网上银行会返回 Cookie 给前端,浏览器将 Cookie 保存下来。
  • 用户在没有登出网上银行的情况下,在浏览器里边打开了一个新的选项卡,然后又去访问了一个危险网站。
  • 这个危险网站上有一个超链接,超链接的地址指向了招商银行网上银行。
  • 用户点击了这个超链接,由于这个超链接会自动携带上浏览器中保存的 Cookie,所以用户不知不觉中就访问了网上银行,进而可能给自己造成了损失。

CSRF 的流程大致就是这样,接下来松哥用一个简单的例子和小伙伴们展示一下 CSRF 到底是怎么回事。

2.CSRF实践

接下来,我创建一个名为 csrf-1 的 Spring Boot 项目,这个项目相当于我们上面所说的网上银行网站,创建项目时引入 Web 和SpringSecurity 依赖,如下:

SpringBoot 如何防御 CSRF 攻击?

创建成功后,方便起见,我们直接将 Spring Security 用户名/密码 配置在 application.properties 文件中:

然后我们提供两个测试接口:

假设 /transfer 是一个转账接口(这里是假设,主要是给大家演示 CSRF 攻击,真实的转账接口比这复杂)。

最后我们还需要配置一下 Spring Security,因为 Spring Security 中默认是可以自动防御 CSRF攻击的,所以我们要把这个关闭掉:

配置完成后,我们启动 csrf-1 项目。

接下来,我们再创建一个 csrf-2 项目,这个项目相当于是一个危险网站,为了方便,这里创建时我们只需要引入 web 依赖即可。

项目创建成功后,首先修改项目端口:

然后我们在 resources/static 目录下创建一个 hello.html ,内容如下:

这里有一个超链接,超链接的文本是点击查看美女图片,当你点击了超链接之后,会自动请求http://localhost:8080/transfer接口,同时隐藏域还携带了两个参数。

配置完成后,就可以启动 csrf-2 项目了。

接下来,用户首先访问 csrf-1 项目中的接口,在访问的时候需要登录,用户就执行了登录操作,访问完整后,用户并没有执行登出操作,然后用户访问csrf-2中的页面,看到了超链接,好奇这美女到底长啥样,一点击,结果钱就被人转走了。

3.CSRF防御

先来说说防御思路。

CSRF 防御,一个核心思路就是在前端请求中,添加一个随机数。

因为在 CSRF 攻击中,黑客网站其实是不知道用户的 Cookie具体是什么的,他是让用户自己发送请求到网上银行这个网站的,因为这个过程会自动携带上Cookie 中的信息。

所以我们的防御思路是这样:用户在访问网上银行时,除了携带Cookie中的信息之外,还需要携带一个随机数,如果用户没有携带这个随机数,则网上银行网站会拒绝该请求。黑客网站诱导用户点击超链接时,会自动携带上Cookie中的信息,但是却不会自动携带随机数,这样就成功的避免掉 CSRF 攻击了。

Spring Security 中对此提供了很好的支持,我们一起来看下。

3.1 默认方案

Spring Security 中默认实际上就提供了 csrf 防御,但是需要开发者做的事情比较多。

首先我们来创建一个新的 Spring Boot 工程,创建时引入 Spring Security、Thymeleaf 和 web 依赖。

SpringBoot 如何防御 CSRF 攻击?

项目创建成功后,我们还是在 application.properties 中配置用户名/密码:

接下来,我们提供一个测试接口:

注意,这个测试接口是一个 POST 请求,因为默认情况下,GET、HEAD、TRACE 以及 OPTIONS 是不需要验证 CSRF 攻击的。

然后,我们在 resources/templates 目录下,新建一个 thymeleaf 模版,如下:

注意,在发送 POST 请求的时候,还额外携带了一个隐藏域,隐藏域的 key 是 ${_csrf.parameterName},value则是${_csrf.token}。

这两个值服务端会自动带过来,我们只需要在前端渲染出来即可。

接下来给前端 hello.html 页面添加一个控制器,如下:

添加完成后,启动项目,我们访问 hello 页面,在访问时候,需要先登录,登录成功之后,我们可以看到登录请求中也多了一个参数,如下:

SpringBoot 如何防御 CSRF 攻击?

可以看到,这里也多了 _csrf 参数。

这里我们用了 Spring Security 的默认登录页面,如果大家使用自定义登录页面,可以参考上面 hello.html的写法,通过一个隐藏域传递_csrf 参数。

访问到 hello 页面之后,再去点击按钮,就可以访问到 hello 接口了。

小伙伴们可以自行尝试在 hello.html 页面中,去掉 _csrf 参数,看看访问 hello 接口的效果。

这是 Spring Security 中默认的方案,通过 Model 将相关的数据带到前端来。

如果你的项目是前后端不分项目,这种方案就可以了,如果你的项目是前后端分离项目,这种方案很明显不够用。

3.2 前后端分离方案

如果是前后端分离项目,Spring Security 也提供了解决方案。

这次不是将 _csrf 放在 Model 中返回前端了,而是放在 Cookie 中返回前端,配置方式如下:

有小伙伴可能会说放在 Cookie 中不是又被黑客网站盗用了吗?其实不会的,大家注意如下两个问题:

理解透了上面两点,你就会发现 _csrf 放在 Cookie 中是没有问题的,但是大家注意,配置的时候我们通过 withHttpOnlyFalse方法获取了CookieCsrfTokenRepository 的实例,该方法会设置 Cookie 中的 HttpOnly 属性为false,也就是允许前端通过 js操作 Cookie(否则你就没有办法获取到 _csrf)。

配置完成后,重启项目,此时我们就发现返回的 Cookie 中多了一项:

SpringBoot 如何防御 CSRF 攻击?

接下来,我们通过自定义登录页面,来看看前端要如何操作。

首先我们在 resources/static 目录下新建一个 html 页面叫做 login.html:

免责声明: 凡标注转载/编译字样内容并非本站原创,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如果你觉得本文好,欢迎推荐给朋友阅读;本文链接: https://m.nndssk.com/dngz/330376grFbXP.html
猜你喜欢
最新应用
热门应用