SpringSecurity(十)【CSRF 漏洞保护】

news2024/12/24 21:01:48

十、CSRF 漏洞保护


简介

CSRF(Cross-Site Request Forgery 跨站请求伪造),也可称为一键式攻击(one-click-attack)通常缩写为 CSRF 或者 XSRFCSRF 攻击是一种挟持用户在当前已登录的浏览器上,发送恶意请求的攻击方法。相对于 XSS 利用用户对指定网站的信任。CSRF则是利用网站对用户网页浏览器的信任。简单来说, CSRF 是致击者通过一些技术手段欺骗用户的浏览器,去访问一个用户曾经认证过的网站并执行恶意请求,例如发送邮件、发消息、甚至财产操作(如转账和购买商品)。由于客户端(浏览器)已经在该网站上认证过,所以该网站会认为是真正用户在操作而执行请求(实际上这个并非用户的本意

  • 举个简单的例子

假设 A 现在登录了某银行的网站准备完成一项转账操作,转账的链接如下:
https://bank.xxx.com/withdraw?account=A&amount=1000&for=B
可以看到。这个链接是想从 A 这个账户下转账1000元到 B 账户下。假设 A 没有注销登录该银行的网站,就在同一个浏览器新的选项卡中打开了一个危险网站,这个危险网站中有一幅图片,代码如下:
< img src=“https://bankxxx.com/withdraw?account=A&amount=1000&for=C”>
一旦用户打开了这个网站,这个图片链接中的请求就会自动发送出去。由于是同一个浏览器并且用户尚未注销登录,所以该请求会自动携带上对应的有效的Cookie信息,进而完成一次转账操作。这就是跨站请求伪造

10.1 CSRF 攻击演示

说明:模拟场景,用户A给用户B转账,在用户A未注销之前,有人通过用户A已经认证的信息,对其进行转账给用户C的操作

搭建:

  • spring-security-11-csrf-bank 服务进行正常银行操作(8080端口)
  • spring-security-11-csrf-attack 用于模拟 csrf 跨站请求(8081端口)

攻击演示

1) spring-security-11-csrf-bank 模块

  1. 创建模块 spring-security-11-csrf-bank,导入依赖pom.xml
<dependencies>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--security-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
  1. 自定义 Security 配置
  • WebSecurityConfigurerAdapter
package com.vinjcent.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {


    // 自定义用户认证数据源(内存方式)
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
        return inMemoryUserDetailsManager;
    }

    // 自定义数据源需要对外暴露
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    // http 认证配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable();     // 关闭 CSRF 跨站请求保护
    }
}
  1. 定义测试controller接口
package com.vinjcent.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/index")
    public String toIndex() {
        return "index ok";
    }

    @PostMapping("/withdraw")
    public String withdraw() {
        System.out.println("第一次转账操作");
        return "执行第一次转账操作";
    }
}

2) spring-security-11-csrf-attack 模块

  1. 创建模块 spring-security-11-csrf-attack,导入依赖pom.xml
<dependencies>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>
  1. 编写index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模拟 CSRF 跨站请求伪造</title>
</head>
<body>

<form action="http://localhost:8080/withdraw" method="post">
    <input type="hidden" name="name" value="A">
    <input type="hidden" name="money" value="B">
    <input type="submit" value="提交">
</form>

</body>
</html>

3)测试

  1. 先在 spring-security-11-csrf-bank 进行登录

在这里插入图片描述

  1. 然后再访问 spring-security-11-csrf-attack 主页进行请求

在这里插入图片描述

  1. 可以看到从8081进行了一次对8080的请求转账

在这里插入图片描述

在这里插入图片描述

小结

可以发现,当用户在8080正常认证身份之后,假如另外一台服务知道8080服务的转账接口,那么就会根据这个接口去操作用户的信息,这回给我们用户带来数据泄露的问题,因为都是在当前网站的 Cookie 信息识别用户

10.2 CSRF 防御

CSRF 攻击的根源在于浏览器默认的身份验证机制(自动携带当前网站的Cookie信息)。这种机制虽然可以保证请求是来自用户的某个浏览器,但是无法确保这请求是用户授权发送。攻击者和用户发送的请求一模一样,这意味着我们没有办法去直接拒绝这里的某一个请求。如果能在合法清求中额外携带一个攻击者无法获取的参数,就可以成功区分出两种不同的请求,进而直接拒绝掉恶意请求。在 SpringSecurity 中就提供了这种机制来防御 CSRF 攻击,这种机制我们称之为令牌同步模式

令牌同步模式

这是目前主流的 CSRF 攻击防御方案。具体的操作方式就是在每一个 HTTP 请求中,除了默认自动携带的 Cookie 参数之外,再提供一个安全的、随机生成的字符串,我们称之为 CSRF 令牌。这个 CSRF 令牌由服务端生成,生成后在 HttpSession 中保存一份。当前端请求到达后,将请求携带的 CSRF 令牌信息和 服务端中保存的令牌进行对比,如果两者不相等,则拒绝掉该 HTTP 请求

考虑到有一些外部站点链接到我们的网站,所以我们要求请求是幂等的,这样对于 HEAD、OPTIONS、TRACE等方法就没有必要使用 CSRF 令牌了,强行使用可能会导致令牌泄露

  • 关闭 CSRF 请求保护的登录页面

在关闭 CSRF 请求保护之后,登陆页面是不会携带一个 csrf 的 token 令牌的

在这里插入图片描述

  • 开启 CSRF 请求保护的登录页面

在开启 CSRF 请求保护之后,登陆页面携带了一个 csrf 的 token 令牌的,并且再次使用8081服务请求,会直接拦截

在这里插入图片描述

在这里插入图片描述

10.3 传统 web 开发使用 CSRF

开启 CSRF 防御后会自动在提交的表单加入如下代码,如果不能自动加入,需要开启之后手动加入如下代码,并随着请求提交。获取服务端令牌方式如下

<input th:name="${_csrf.parameterName}" type="hidden" th:value="{_csrf.token}" />

环境搭建

  1. 依赖pom.xml
<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--thymeleaf-security-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>
  1. application.yml配置文件
server:
  port: 8080

spring:
  thymeleaf:
    mode: HTML
    suffix: .html
    prefix: classpath:/templates/
    cache: false
  1. 开发测试 controller
package com.vinjcent.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {

    @PostMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello spring security!";
    }


    @RequestMapping("/toIndex")
    public String toIndex() {
        return "index";
    }
}
  1. 创建 index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>测试 CSRF 防御(传统web方式)</title>
</head>
<body>
<form th:action="@{/hello}" method="post">
    <input type="submit" value="提交">
</form>
</body>
</html>
  1. Security 配置
package com.vinjcent.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest()
                .authenticated()
                .and().formLogin()
                .and().csrf();	// 开启 csrf 跨域请求保护
    }
}
  1. 测试
  • 在没有任何配置情况下,security 配置开启了 csrf 请求保护,传统的 web 开发会自动在表单中添加一个表单项 _csrf,如图所示

在这里插入图片描述

10.4 前后端分离使用 CSRF

前后端分离时,只需要将生成 csrf 放入 Cookie 中,并在请求时获取 Cookie 中令牌信息进行提交即可

模拟前后端分离

在已有的前后端分离认证中,修改 Security 配置,核心代码如下

package com.vinjcent.config.security;

import com.vinjcent.filter.LoginFilter;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    // 使用内存数据源
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
        return inMemoryUserDetailsManager;
    }

    // 配置认证管理者的认证数据源
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    // 暴露自定义认证数据源
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 创建自定义的LoginFilter对象
    @Bean
    public LoginFilter loginFilter() throws Exception {
        
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setFilterProcessesUrl("/login");
        loginFilter.setUsernameParameter("uname");
        loginFilter.setPasswordParameter("passwd");
        loginFilter.setAuthenticationManager(authenticationManager());
        loginFilter.setAuthenticationSuccessHandler(new DivAuthenticationSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(new DivAuthenticationFailureHandler());
        return loginFilter;
    }

    // 请求拦截配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                // .disable();
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());    // 将令牌保存到 cookie 中,允许 cookie 前端获取

        // 替换原始 UsernamePasswordAuthenticationFilter 过滤器
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

测试

  • 第一次登录,登陆失败,原因是需要一个 csrftoken 令牌

在这里插入图片描述

  • 同时,在 Cookie 中生成了XSRF-TOKEN的key-value,如下图所示

在这里插入图片描述

解析 csrf 认证流程

  • 进行 Debug 调式

在这里插入图片描述

  • 可以看到有些请求类型不需要 token

在这里插入图片描述

  • 需要先获取请求头,默认值为X-XSRF-TOKEN

在这里插入图片描述

  • 首先会去请求头Header中获取,如果获取不到,就会去请求参数(_csrf)中获取

在这里插入图片描述

  • 最后将实际的 token 与当前的 token 进行比对

在这里插入图片描述

  • 最后发现,如果需要实现前后端分离的 csrf 功能,要么在请求参数中添加一个名为 _csrf的参数或请求头 header 中携带一个X-XSRF-TOKEN键值对key-value

在这里插入图片描述

  • 认证成功展示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python绘制地磁场

文章目录简介磁场绘制简介 为国际参考磁场对Python的封装&#xff0c;可通过经纬高度以及时间来计算地磁场强度&#xff0c;使用方法简单粗暴&#xff0c;如下 import pyIGRF pyIGRF.igrf_value(lat, lon, alt, date)参数含义为 lat 纬度lon 经度alt 海拔date 日期&#xff…

vuejs中组件的两种不同的编写风格-选项式API及组合式API

前言随着vue3的逐渐稳定,以及周边生态的完善,现在vue3已经成为默认的使用方式了的所以,对于一个前端开发者,Vue2与Vue3都得要会,在vue3中新增很多东西,比如:Fragment,Teleport,Suspense,也去掉了vue2中一些特性,比如:移除keyCode支持作为v-on的修饰符等在编程风格上也有一些区别…

Java项目:springBoot+Vue汽车销售管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目基于spring boot以及Vue开发&#xff0c;为前后端分离的项目。针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等…

【Python百日进阶-数据分析】Day138 - plotly甘特图:px.timeline()

文章目录一、语法二、参数三、返回值四、实例4.1 带有 plotly.express 的甘特图和时间表4.1.1 普通甘特图4.1.2 px.timeline 的离散颜色4.1.3 px.timeline 的连续颜色4.1.4 同一水平线上有多个条4.1.5 Dash中使用甘特图一、语法 甘特图是一种条形图&#xff0c;用于说明项目进…

【C++高阶数据结构】并查集

&#x1f3c6;个人主页&#xff1a;企鹅不叫的博客 ​ &#x1f308;专栏 C语言初阶和进阶C项目Leetcode刷题初阶数据结构与算法C初阶和进阶《深入理解计算机操作系统》《高质量C/C编程》Linux ⭐️ 博主码云gitee链接&#xff1a;代码仓库地址 ⚡若有帮助可以【关注点赞收藏】…

Linux之top命令详解

Linux之top命令详解 一、简单介绍 top是Linux性能分析工具&#xff0c;显示系统占用资源情况&#xff0c;和windows的任务管理器一样。top动态显示进程暂用资源情况&#xff0c;top对系统处理器的状态监视&#xff0c;它将显示CPU任务列表&#xff0c;按照CPU使用、内存使用和…

You are not allowed to create a user with GRANT

8.0之后的mysql不支持授权的时候就进行用户创建&#xff0c;所以创建之后才能授权; USE mysqlSELECT USER, PASSWORD, HOST FROM USER;SELECT USER ,grant_priv FROM USERCREATE USER zjy IDENTIFIED BY 123456; #host默认是%GRANT ALL PRIVILEGES ON *.* TO zjy% MySql-Ser…

【正点原子I.MX6U-MINI移植篇】rootfs移植过程详解(三)

Linux三巨头己经完成了2个了&#xff0c;就剩最后一个rootfs&#xff08;根文件系统&#xff09;了&#xff0c;根文件系统的组成以及如何构建根文件系统是Liux移植的最后一步&#xff0c;根文件系统构建好以后就意味着我们己经拥有了一个完整的、可以运行的最小系统。以后我们…

智慧工地车辆未冲洗抓拍系统 opencv+yolo

智慧工地车辆未冲洗抓拍系统利用opencvyolo网络深度学习架构模型对现场画面中车辆的冲洗情况实现智能识别。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Python API&#xff0c;结合了OpenCV CAPI和Python语言的最佳特性。O…

微信防撤回功能修改

今天无意之中看到了一个帖子&#xff0c;谈到了有关微信消息撤回的。突发奇想实现一下&#xff0c;以后就不怕错过朋友的消息了。 首先介绍一下基本思路&#xff0c;由于微信采用的是CS端原理&#xff0c;所有的数据请求均通过服务器&#xff0c;客户端只是响应指令而已。 A向…

实验三:自主存取控制实验

【实验目的】 掌握自主存取控制权限的定义和维护方法。掌握在ORACLE数据库中定义用户、角色&#xff0c;分配权限给用户、角色&#xff0c;回收权限&#xff0c;以相应用户登录数据库验证权限分配是否正确的方法。 【实验内容】 设有一个企业&#xff0c;由总裁负责管理采购、…

【Pandas指南】Series

Pandas数据结构简介 - Series 来源&#xff1a;Pandas官网&#xff1a;https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html 笔记托管&#xff1a;https://gitee.com/DingJiaxiong/machine-learning-study 下面将从对 pandas 中的基本数据结构进行快速…

Git Bash Here和RStudio软件的问题解决

Git Bash Here和RStudio软件的问题解决 文章目录Git Bash Here和RStudio软件的问题解决0、 写在前面1、Git软件在任务栏图标空白2、RStudio软件2.1 警告信息InormalizePath(path.expand(path),winslash,mustWork)2.2 incomplete final line found by readTableHeader on报错3、…

推荐两个go语言的websocket库

最近在写一个需要前后端保持通信的服务。前端要能及时感知后端数据的变化&#xff0c;后端要及时处理前端发过来的指令。这种服务就需要用到websocket了。 以前在写websocket相关的程序时&#xff0c;一直在用gorilla/websocket这个库&#xff0c;这个库事实上已经成为了go语言…

后端面试之系统设计 - 用户密码如何储存在DB里

原文地址&#xff1a;码农在新加坡的个人博客 背景 现在很多网站都因为爆库导致密码泄漏&#xff0c;要设计怎么样的密码储存机制&#xff0c;才能保证最大限度的不被盗取&#xff0c;即使数据泄漏&#xff0c;黑客也无法在短时间内获取对应的密码来登录用户的账号&#xff0c…

LeanCloud: 数据存储实现小程序云开发

1. LeanCloud 官网传送 2. LeanCloud选择原因 微信小程序的开发包括上线需要一个备案过的域名&#xff0c;而域名备案又需要一个服务器&#xff08;仅腾讯云而言&#xff09;。而微信云开发作为个人开发者受限于费用也不做考虑。 此时不考虑复杂的业务逻辑数据库读取是后端服…

A股、港股上市公司碳排放、碳强度和碳披露数据(2018-2021年)

随着中国碳强度减排任务的不断加重&#xff0c;当前政策的就业红利将不复存在&#xff0c;同时政策机制蕴含的资源错配、各行业边际减排成本不相等的问题则愈加严重&#xff0c;实施碳交易减排政策的时机逐渐成熟&#xff0c;政府应如何根据二氧化碳排放量、碳强度和碳披露等数…

带你走进Java字符串的小世界

目录 一. String 1. 什么是String 2. String常用构造器 3. 字符串的判断 4. 字符串的获取 5. 字符串的转换 6. 字符串比较和替换 7. 字符串的切割 二. StringBuffer与StringBuilder 2.1 关于StringBuffer 2.1.1 定义 2.1.2 构造方法 2.2 关于StringBuffer 三. StringJoiner的使…

分布式缓存的四大痛点

目前开发中经常用到的缓存&#xff0c;是我们必不可缺的&#xff0c;他大大的提高了我们整个项目的响应速度和并发量。但是带来好处的同时&#xff0c;也给我们带了了新的问题&#xff1a;缓存穿透、缓存击穿、缓存雪崩以及缓存一致性这么四个问题&#xff0c;也是分布式缓存的…

LeetCode算法之----动态规划

点赞收藏&#xff0c;以防遗忘 本文【程序大视界】已收录&#xff0c;关注免费领取互联网大厂学习资料&#xff0c;添加博主好友进群学习交流&#xff0c;欢迎留言和评论&#xff0c;一起交流共同进步。 目录 【一】前言 【二】打家劫舍 【三】不同路径 【四】最小路径和 …