Spring Securtity (九)会话固定攻击和跨站请求伪造

本文介绍了HttpSession的工作原理,浏览器关闭后的session失效原因,会话固定攻击的原理及防范措施,以及SpringSecurity如何自动防止CSRF攻击。重点讲解了如何通过配置避免会话固定攻击和使用Cookie CsrfTokenRepository来增强安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 什么是HttpSession

  1. HttpSession 是一个服务端的概念,服务端生成的 HttpSession 都会有一个对应的 sessionid,这个sessionid 会通过 cookie 传递给前端,前端以后发送请求的时候,就带上这个 sessionid 参数,服务端看到这个 sessionid 就会把这个前端请求和服务端的某一个 HttpSession 对应起来,形成“会话”的感觉
    2.浏览器关闭并不会导致服务端的 HttpSession 失效,想让服务端的 HttpSession 失效,要么手动调用HttpSession#invalidate 方法;要么等到 session 自动过期;要么重启服务端。
    3.之所以说浏览器关闭之后session失效是因为浏览器关闭之后,保存在浏览器里面的sessionid就丢失了,所以当浏览器再次访问服务端的时候,服务端就会给浏览器重新分配一个sessionid,这个sessionid和之前的HttpSession对应不上,所以就导致用户感觉session失效。
    4.我们可以通过手动配置让浏览器重启之后sessionid不丢失,但是有隐患。
    以spring Boot 为例,服务端生成sessionid后,传给前端的响应头如下
    在这里插入图片描述
    在服务端的响应头中有一个 Set-Cookie 字段,该字段指示浏览器更新 sessionid,同时大家注意还有一个 HttpOnly 属性,这个表示通过 JS 脚本无法读取到 Cookie 信息(这个有大用),这样能有效的防止 XSS 攻击。下一次浏览器再去发送请求的时候,就会自觉的携带上这个 jsessionid 了:
    在这里插入图片描述

二.会话固定攻击(session fixation attack)

正常来说,只要你不关闭浏览器,并且服务端的HttpSession也没有过期,那么维系服务端和浏览器的sessionid是不会发生变化的,而会话固定攻击,就利用了这一点**,借助用户用相同的会话ID获取认证和授权**,然后利用该会话ID劫持用户的会话一冒充用户,从而造成会话固定攻击用户,造成会话固定攻击。

以淘宝为例:解析会话固定攻击的步骤:
1.攻击者自己可以正常访问淘宝网,在访问过程中,淘宝网给攻击者分配了一个sessionid
2.攻击者利用拿到的sessionid构造了一个淘宝网站的连接,并且把该连接发送而给受害者。
3.用户使用该仿造的连接登录淘宝网站(该连接中含有攻击者之前拿到的sessionid),用户登录时候,淘宝网站一看你有sessionid那我就不用重新给你生成sessionid了直接登录吧,就这样用户登录成功,一个合法的会话就成功建立。
4.然后攻击者就可以利用自己手里的sessionid去冒充用户。

(补充:如果这个过程中,淘宝网支持URL重写,那么攻击会变得更加容易。)
什么是URL重写

即,用户在浏览器中禁用了cookie,那么也就禁用了sessionid,所以有的服务端就支持把sessionid放在请求地址中
如下
http://www.taobao.com;jsessionid=xxxxxx
这个让我想到了什么?是不是我们的shiro中,登录时偶尔会产生这样的情况(现在shiro已经优化了这个玩意),这也是spring Security比shiro更加有优势的地方,他自动的做了防止固定会话攻击和csrf攻击的操作。

2.1 如何防止会话固定攻击

这个问题的根源在 sessionid 不变如果用户在未登录时拿到的是一个 sessionid,登录之后服务端给用户重新换一个 sessionid,就可以防止会话固定攻击了。如果你使用了 Spring Security ,其实是不用担心这个问题的,因为 Spring Security 中默认已经做了防御工作了。

1.给请求地址中带上 ; 请求就会直接被拒绝。
2.是响应的 Set-Cookie 字段中有 HttpOnly 属性,这种方式避免了通过 XSS 攻击来获取
Cookie 中的会话信息进而达成会话固定攻击。
3.让 sessionid 变一下。既然问题是由于 sessionid 不变导致的,那我就让 sessionid 变一
下。

具体配置:
在这里插入图片描述
可以看到,在这里,我们有四个选项:
1.migrateSession 表示在登录成功之后,创建一个新的会话,然后讲旧的 session 中的信息复制到新的 session 中,默认即此。
2. none 表示不做任何事情,继续使用旧的 session。
3. changeSessionId 表示 session 不变,但是会修改 sessionid,这实际上用到了 Servlet 容器提供的防御会话固定攻击。
4. newSession 表示登录后创建一个新的 session。

注意:
默认的的 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,又是另外一个 sessionid,这样就可以有效避免会话固定攻击。这三种方案,可以让我们有效避免会话固定攻击!

三.Csrf攻击

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

3.1CSRF 原理

在这里插入图片描述

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

3.2 项目配置

application.properties


spring.security.user.name=javaboy 
spring.security.user.password=123

service

@RestController public class HelloController { @PostMapping("/transfer")
 public void transferMoney(String name, Integer money) 
 {
  System.out.println("name = " + name); System.out.println("money = " + money);
   }

@GetMapping("/hello") 
public String hello()
 {
  return "hello"; 
  } 
  }

假设 /transfer 是一个转账接口(这里是假设,主要是给大家演示 CSRF 攻击,真实的转账接口比这复杂)。
最后我们还需要配置一下 Spring Security,因为 Spring Security 中默认是可以自动防御 CSRF 攻击的,所以我们要把这个关闭掉:

@Configuration 
public class SecurityConfig extends WebSecurityConfigurerAdapter 
{ 
@Override
 protected void configure(HttpSecurity http) throws Exception { 
 http.authorizeRequests().anyRequest()
 .authenticated()
  .and() 
  .formLogin()
   .and() 
   .csrf()
    .disable(); 
    } 
    }

配置完成后,我们启动 csrf-1 项目。
接下来,我们再创建一个 csrf-2 项目,这个项目相当于是一个危险网站,为了方便,这里创建时我们只
需要引入 web 依赖即可。
项目创建成功后,首先修改项目端口:

server.port=8081

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

<body>

 <form action="http://localhost:8080/transfer" method="post"> 
 <input type="hidden" value="javaboy" name="name">
  <input type="hidden" value="10000" name="money"> 
  <input type="submit" value="点击查看美女图片"> 
  </form>
   </body>

这里有一个超链接,超链接的文本是点击查看美女图片,当你点击了超链接之后,会自动请求
http://localhost:8080/transfer 接口,同时隐藏域还携带了两个参数。
配置完成后,就可以启动 csrf-2 项目了。
接下来,用户首先访问 csrf-1 项目中的接口,在访问的时候需要登录,用户就执行了登录操作,访问完
整后,用户并没有执行登出操作,然后用户访问 csrf-2 中的页面,看到了超链接,好奇这美女到底长啥
样,一点击,结果钱就被人转走了。

3.3 CSRF防御

先来说说防御思路。
CSRF 防御,一个核心思路就是在前端请求中,添加一个随机数。
因为在 CSRF 攻击中,黑客网站其实是不知道用户的 Cookie 具体是什么的,他是让用户自己发送请求
到网上银行这个网站的,因为这个过程会自动携带上 Cookie 中的信息。
所以我们的防御思路是这样:用户在访问网上银行时,除了携带 Cookie 中的信息之外,还需要携带一
个随机数,如果用户没有携带这个随机数,则网上银行网站会拒绝该请求。黑客网站诱导用户点击超链
接时,会自动携带上 Cookie 中的信息,但是却不会自动携带随机数,这样就成功的避免掉 CSRF 攻击
了。
Spring Security 中对此提供了很好的支持,我们一起来看下。

3.4总结

csrf攻击原理:csrf:利用浏览器自动携带上cookie机制(用户登录qq登录界面,登录上去了,自己的cookie存储在了浏览器,然后访问盗号网站,访问了他的接口(带了校验参数的接口),浏览器携带着自己的cookie去他的接口进行判断,然后盗号网站的接口就拿到了你cookie里面的账户信息)

防止攻击:spring security怎么防止csrf攻击,加了一个csrf的临牌参数,当用户进行put,post,insert的时候必须携带上自己的令牌参数csrf,否则没有办法去登录接口(你说你自己的不带csrf都访问不了,别人的盗号接口没你的csrf令牌能访问吗?)

注意:
1.get请求只是做查询的,一般都设置为游客都可访问,进入get请求的时候当然不用携带csrf令牌参数
2.为什么手机app没办法csrf攻击:没有csrf参数令牌

具体代码如下:
application

spring.security.user.name=javaboy 
spring.security.user.password=123

在 resources/templates 目录下,新建一个 thymeleaf 模版

<body>
 <form action="/hello" method="post">
 <input type="hidden" th:value="${_csrf.token}" th:name="${_csrf.parameterName}">
  <input type="submit" value="hello"> </form>
   </body>

service

@GetMapping("/hello")
 public String hello2()
  { 
  return "hello";
   }

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

在这里插入图片描述

而这个参数就是我们总结上提到的csrf令牌。

四.Spring Security中实现前后端分离方案

这次不是将 _csrf 放在 Model 中返回前端了,而是放在 Cookie 中返回前端

@Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter
  { 
  @Override
   protected void configure(HttpSecurity http) throws Exception 
   {
   http.authorizeRequests()
   .anyRequest()
   .authenticated() 
   .and()
    .formLogin() 
    .and() .csrf()
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
     } 
     }

那把csrf令牌放在Cookied中安全吗,会被攻击者盗取吗
1.黑客网站根本不知道你的 Cookie 里边存的啥,他也不需要知道,因为 CSRF 攻击是浏览器自动携带上 Cookie 中的数据的。
2.我们将服务端生成的随机数放在 Cookie 中,前端需要从 Cookie 中自己提取出来 _csrf 参数,然后拼接成参数传递给后端,单纯的将 Cookie 中的数据传到服务端是没用的。
3.js在这里面做了很多,利用js把csrf临牌从cookie中读取出来,放到你请求的参数里,再发送到服务端,而这个攻击是利用cookie一股脑的发送到盗号接口但是并不会把这一些参数解析,所有这么写也是没有问题的

配置完成后,重启项目,此时我们就发现返回的 Cookie 中多了一项:
在这里插入图片描述

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

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

<!DOCTYPE html> 
<html lang="en"> 
<head>
<meta charset="UTF-8"> <title>Title</title>
 <script src="js/jquery.min.js"></script>  <script src="js/jquery.cookie.js"></script>
  </head>
 <body> 

<div>
<input type="text" id="username">
 <input type="password" id="password"> <input type="button" value="登录" id="loginBtn"> 
 </div> 
 <script> 
 $("#loginBtn").click(function () { 
let _csrf = $.cookie('XSRF-TOKEN'); $.post('/login.html', {username:$("#username").val(),password:$("#password").val(),_csrf:_csrf},functi on (data) {
 alert(data);
  }) 
  }) 
  </script>
   </body>
    </html>

这段html做了什么:

1 首先引入 jquery 和 jquery.cookie ,方便我们一会操作 Cookie。
2. 定义三个 input,前两个是用户名和密码,第三个是登录按钮。
3. 点击登录按钮之后,我们先从 Cookie 中提取出 XSRF-TOKEN,这也就是我们要上传的 csrf 参数。
4. 通过一个 POST 请求执行登录操作,注意携带上 _csrf 参数。

服务端我们也稍作修改,如下:

@Configuration 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 
@Override 
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**"); 
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and() 
.formLogin() 
.loginPage("/login.html") .successHandler((req,resp,authentication)->{ 
resp.getWriter().write("success");
 })
.permitAll() 
.and()

.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
 }

一方面这里给 js 文件放行。
另一方面配置一下登录页面,以及登录成功的回调,这里简单期间,登录成功的回调我就给一个字符串就可以了。

在这里插入图片描述
如上,我们的 _csrf 配置已经生效了

本篇文章参考:

23
22
21
还是那句话,多看大佬文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值