SpringMVC/boot-CSRF安全方案
1. CSRF原理与防御方案概述
一. 原理
增删改的接口参数值都有规律可循,可以被人恶意构造增删改接口
将恶意构造的增删改接口发给对应特定用户,让特定用户点击
特定用户使用自己的认证信息对该接口发起了请求,可能被新增危险信息(比如管理员账号),修改敏感信息(比如退款金额),删除关键信息(比如删除差评)
二. 防御方案概述
参数不可猜解,发起请求时在参数中增加随机token参数
token参数在后台与保存在cookie,session,tair中的token参数进行比对,若不匹配或者没有该参数,则校验不通过
黑客无法获取到特定用户的随机token值,所以杜绝CSRF的危害
2 ali修复方案
一 . 确认应用类型
若为正常业务,提供数据/文件等增删改服务,则需要配置CSRF,请继续看下去
若应用不是Web应用,或者只是HSF服务或者给其他应用服务器调用的API接口服务(纯内网的纯Server to Server,不是通过Login获取登陆态的接口,而是通过AKSK加签名验证签名) ,则不需要配置CSRF,请提供相应加签验签代码给对应答疑或者安全工程师进行确认并关闭漏洞。
二 . 安全包引入
Step1. 配置扩展包POM依赖
注意:SpringMVC扩展安全包引入后会默认开启一系列开关,包括XSS开关,CSRF开关等,从而导致业务短暂出现异常(前端页面乱码,接口访问返回403状态码等),只需要继续按照文档操作下去,业务最终会恢复正常。
1. SpringMVC扩展包
请参考网上SpringMVC安全扩展引入文档
2 SpringBoot扩展包 (starter)
请参考网上SpringBoot安全扩展引入文档
Step2. 查看是否依赖成功
POM配置完成后,在IDEA的External Libraries中查找是否以下包存在
//SpringMVC仅检查这个包
com.alibaba.security:security-spring-webmvc
//SpringBoot仅检查这个包
com.alibaba.security:security-spring-boot-starter
三 . CSRF开关配置
Step1. 显式配置CSRF开关
虽然CSRF在安全包引入之后,会自动开启CSRF拦截,但是为了确保配置可读性以及后续问题排查方便,请在resources下的"application.properties" (若没有,请创建)文件中协商如下配置:
spring.security.csrf.enabled = true
注:开启了该开关之后,所有访问请求都会因为没有token带入被拦截导致访问不成功,需要继续配置下去,让业务恢复正常
Step2. 将token带给前端
配置好开关之后,安全包会生成一个随机字符串,我们称为CSRF_Token,该token会被默认存入cookie中。若使用VM,则可以通过VM调用相关接口获得。
这个Token需要在前端的每个增删改接口请求中作为参数带入给服务器用于校验安全性
不同前端技术方案有不同带入方式:
1 VM后端模板
a. VM后端模板有三种token带入请求的配置方式:
- 在application.properties中统一配置
- CSRF Token 自动生成的URL映射列表,多值使用逗号分隔(默认值为空)
- 当前URL风格为ant风格,风格值由配置项 spring.security.csrf.url.style 决定
spring.security.csrf.token.urls = /csrf_token/**
b. 在Controller类级别使用注解@CsrfTokenModel配置
@Controller
@RequestMapping("/csrf")
@CsrfTokenModel
public class CsrfController {
@RequestMapping("/form")
public String form() {
return "csrf_form";
}
}
c.在Controller类级别使用注解配置
此种情况可以使得该controller下所有模板渲染。都可以通过宏获取token的参数名称和值
在方法级别使用注解@CsrfTokenModel配置
@Controller
@RequestMapping("/csrf")
public class CsrfController {
@RequestMapping("/form")
@CsrfTokenModel
public String form() {
return "csrf_form";
}
}
如果需要CSRF Token校验的Controller或者方法过多时,当前框架还提供一种便利的方式, 即URL映射级别的自动生成方式,只需在application.properties文件中增加如下配置:
CSRF Token 自动生成的URL映射列表,多值使用逗号分隔(默认值为空)
当前URL风格为正则表达式,风格值由配置项 spring.security.csrf.url.style 决定
spring.security.csrf.token.urls = /csrf_token/**
- CSRF Token 模型属性名称
spring.security.csrf.token.model.attribute = csrfToken
后端配置好之后,在VM模板中,针对所有请求form表单,增加对应字段,确保每次请求都能带上
- Velocity Template Code
<form method="post" action="/form/submit">
<input type="hidden" name="${csrfToken.parameterName}" value="${csrfToken.token}">
<input type="text" name="name"/>
<br>
<input type="submit" value="Submit"/>
</form>
- 渲染后的HTML
<form method="post" action="/form/submit">
<input type="hidden" name="_csrf" value="bfe23341-b28c-41a3-bed8-dfbd65385fc8">
<input type="text" name="name"/>
<br>
<input type="submit" value="Submit"/>
</form>
正常情况下如上图所示,渲染后,字段name为p_csrf, value为随机生成的值,同时会在cookie中放入对应字段。请注意,token的字段名一定是要从csrftoken这个obj中取出来的,不能在前端自定义,若要在后端更换字段名,请参考下面的『CSRF定制化功能』
2. ajax前后端分离
ajax发起请求的情况下,token无法直接渲染到页面上,通过下方途径解决该问题。
- 在cookie中读取token,将其带入到ajax请求的参数中。然后传到后端(Cookie中token的key默认为XSRF-TOKEN)
- 若cookie中的XSRF-TOKEN值无法被js读取,请检查该值httponly属性未true,若为true,请在"application.properties"中新增一个配置项,如下:
- 如果是在参数中携带,默认Token名称是_csrf,如果是在header中携带,默认Token名称是X-XSRF-TOKEN
spring.security.csrf.cookieHttpOnly = false
- 设置完成之后,请清除浏览器缓存之后重新尝试获取XSRF-TOKEN值
3. 跨域下的token传输
在一般业务场景下,安全包会将token种到服务端对应的域名cookie下,可以被前端js调用和植入到header或者参数中。但是在跨域场景下,前端页面与后端服务端不是同一个域名,导致无法取到服务端域名下的cookie。
假设aaaa.com要跨域访问bbbb.com的接口,bbbb.com的接口做了csrf校验。此时按照如下步骤进行token交互:
开启CORS跨域头的业务解决方案如下:
1. bbbb.com新增一个接口,返回自身的csrf token
该接口实现示例如下:
@RequestMapping(value = "/ajax", produces = MediaType.APPLICATION_JSON_VALUE)
@CrossOrigin(origins = "http://aaaaaa.com:7001", maxAge = 3600)
@ResponseBody
public CsrfToken getCsrfToken(HttpServletRequest request, HttpServletResponse response) {
CsrfToken csrfToken = csrfTokenRepository.loadToken(request);
if (csrfToken == null) {
csrfToken = csrfTokenRepository.generateToken(request);
csrfTokenRepository.saveToken(csrfToken, request, response);
}
return csrfToken;
}
2. aaaa.com携带with-credentials的头部来获取该token
function callOtherDomain(){
var xhr = new XMLHttpRequest();
if(xhr) {
xhr.open('GET', 'http://bbbbbb.com:7001/csrf/ajax', true);
xhr.withCredentials = true;
xhr.onload = function () {
result.innerHTML = xhr.responseText;
var json = JSON.parse(xhr.responseText);
token_key = json.paramterName;
token = json.token;
};
xhr.send(null);
}
}
3. 将获取到的token存放在客户端上(比如localstorage,或者页面隐藏字段中)
4. aaaa.com 访问bbbb.com其他接口的时候,获取token作为接口参数/头部参数传递给bbbb.com,同时访问该接口时应该设置withcredentials = true:
function buttonClick(token_key, token){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://bbbbbb.com:7001/csrf/cors/check');
xhr.withCredentials = true;
xhr.onload = function () {
result.innerHTML = xhr.responseText;
};
xhr.onerror = function () {
result.innerHTML = "Error!";
}
xhr.send(token_key + '=' + token + '&' + otherparams)
}
Step3. 后端进行Token校验
后端进行token校验,目前只提供全局url检查方式,在没有显式配置情况下默认对所有POST请求进行token检查,为了更好的对业务进行支持,建议在classpath下的application.properties进行显式配置,如下:
//根据业务需求进行配置是否拦截GET请求,安全要求POST请求必须拦截
spring.security.csrf.supportedMethods = POST,GET
//使用ant风格配置需要进行token检查的url(安全要求对所有增删改进行token校验)
spring.security.csrf.url.included = /**
//使用ant风格配置无需需要进行token检查的url,只能对查询接口进行excluded
spring.security.csrf.url.excluded = /csrf/nocheck
校验之后,若成功,则会顺利执行对应后台功能,若失败,则会返回403的状态码或者301跳转taobao.error的情况,如下:
status : 403
message : Invalid CSRF Token '' was found on the request parameter 'p_csrf' or header 'h_csrf'.
===
status: 301
location: err2.taobao.com
SpringMVC/boot-CSRF安全方案的更多相关文章
- spring boot 自动部署方案
现在主流的自动部署方案大都是基于Docker的了,但传统的自动部署方案比较适合中小型公司,下面的方案就是比较传统的自动部署方案. 1.为什么需要自动部署 基于微服务的架构,自动部署显得非常重要.因为每 ...
- Java springmvc 统一异常处理的方案
前言:为什么要统一异常处理?经常在项目中需要统一处理异常,将异常封装转给前端.也有时需要在项目中统一处理异常后,记录异常日志,做一下统一处理. Springmvc 异常统一处理的方式有三种. 一.使用 ...
- Jenkins spring boot 自动部署方案
原文地址:http://www.cnblogs.com/skyblog/p/5632869.html 现在主流的自动部署方案大都是基于Docker的了,但传统的自动部署方案比较适合中小型公司,下面的方 ...
- spring boot动态数据源方案
动态数据源 1.背景 动态数据源在实际的业务场景下需求很多,而且想要沟通多数据库确实需要封装这种工具,针对于bi工具可能涉及到从不同的业务库或者数据仓库中获取数据,动态数据源就更加有意义. 2.依赖 ...
- Spring Boot统一异常处理方案示例
一.异常处理的原则 1.调用方法的时候返回布尔值来代替返回null,这样可以 NullPointerException.由于空指针是java异常里最恶心的异常. 2. catch块里别不写代码.空ca ...
- [加密]ESP32 -Secure Boot 安全方案
转自:https://blog.csdn.net/espressif/article/details/79362094 Secure Boot 功能概述 方案概述 Secure Boot 的目的是保证 ...
- SpringMVC -- 梗概--壹
1.springMVC:MVC开源框架 2.springMVC开发流程: 2.1 导包: 2.2 配置前端控制器(核心) DispatcherServlet <servlet> <s ...
- AVR单片机的BOOT区
BOOT区的由来基于一个简单的道理,即单片机的程序是保存在FLASH中的,要运行程序就必须不停的访问FLASH存储器.对于一般的FLASH存储器,数据的写入需要一定的时间来完成,在数据写入完成之前,存 ...
- 使用Spring Boot Actuator、Jolokia和Grafana实现准实时监控
由于最近在做监控方面的工作,因此也读了不少相关的经验分享.其中有这样一篇文章总结了一些基于Spring Boot的监控方案,因此翻译了一下,希望可以对大家有所帮助. 原文:Near real-time ...
- 单片机成长之路(avr基础篇)- 003 AVR单片机的BOOT区
BOOT区的由来基于一个简单的道理,即单片机的程序是保存在FLASH中的,要运行程序就必须不停的访问FLASH存储器.对于一般的FLASH存储器,数据的写入需要一定的时间来完成,在数据写入完成之前,存 ...
随机推荐
- shell下读取文件数据
参考:https://www.imzcy.cn/1553.html while和for对文件的读取是有区别的: 1. for对文件的读是按字符串的方式进行的,遇到空格什么后,再读取的数据就会换行显示 ...
- shell 中()、[]、{}、(())、[[]]等各种括号的使用
11 shell中内置关键字[[]]:检查条件是否成立 1.小括号.圆括号() 1.1 单小括号() 用途 命令组 括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余 ...
- webpack(10)webpack-dev-server搭建本地服务器
前言 当我们使用webpack打包时,发现每次更新了一点代码,都需要重新打包,这样很麻烦,我们希望本地能搭建一个服务器,然后写入新的代码能够自动检测出来,这时候就需要用到webpack-dev-ser ...
- 密码学系列之:memory-bound函数
密码学系列之:memory-bound函数 目录 简介 内存函数 内存受限函数 内存受限函数的使用 简介 memory-bound函数可以称为内存受限函数,它是指完成给定计算问题的时间主要取决于保存工 ...
- java基础---类和对象(2)
一.继承(extends) 面向对象的三大特性之一,当多个类中存在相同属性和行为时,将这些内容抽取到一个公共类中,让多个类(子类)吸收公共类(父类.超类)中已有特征和行为,而在多个类型只需要编写自己独 ...
- CQOI 2021 游记
CQOI 2021 游记 Stage -1 \(\texttt{NOIP}\) 考的比较爆炸所以觉得自己没啥指望了. Stage 0
- libzip开发笔记(二):libzip库介绍、ubuntu平台编译和工程模板
前言 Qt使用一些压缩解压功能,选择libzip库,libzip库比较原始,也是很多其他库的基础支撑库,编译过了windows版本,有需求编译一个ubuntu版本的,交叉编译需求的同样可参照本文章 ...
- C语言:宏定义
#include <stdio.h> #define PI 3.14159265454454235432453245 main() { printf("%f\n",PI ...
- python -- 正则表达式&re模块(转载)
1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...
- js实现0ms延时定时器的几种方式
这两天看到一篇介绍<如何实现准时的 setTimeout?>的文章,文章起源于一道面试题:有什么办法让setTimeout准时呀?具体文章内容可查看附录[1],看完之后,引起了我对setT ...