目录
CSRF (Cross-Site Request Forgery 跨站请求伪造),也可称为一键式攻击 (one-clike-attack),通常缩写为 CSRF或者XSRF。
CSRF 攻击是一种挟持用户在当前已登录的浏览器上发送恶意请求的攻击方法。相对于XSS 利用用户对指定网站的信任,CSRF 则是利用网站对用户网页浏览器的信任。简单来说,CSRF是致击者通过一些技术手段欺骗用户的浏览器,去访问一个用户曾经认证过的网站并执行恶意请求,例如发送邮件、发消息、甚至财产操作(如转账和购买商品)。由于客户端(浏览器)已经在该网站上认证过,所以该网站会认为是真正用户在操作而执行请求(实际上这个并非用户的本意)
举例说明:
假设 小明 现在登录某银行的网站准备完成一项转账操作,转账的连接如下:
https: //bank .xxx .com/withdraw?account=blr&amount=1000&for=zhangsan
可以看到,这个连接是想从 小明 这个账户下转账 1000 元到 zhangsan 账户下,假设 小明没有注销登录该银行的网站,就在同一个浏览器新的选项卡中打开一个危险网站,这个危险网站中有一副图片,代码如下:
<img src="https: //bank .xxx .com/withdraw?account=blr&amount=1000&for=lisi" />
一旦用户打开了这个网站,这个图片链接中的请求就会自动发送出去。由于是同一个浏览器并且用户尚未注销登录,所以该请求会自动携带上对应的有效的 Cookie 信息,进而成功完成一次转账操作。这就是跨站请求伪造。
介绍
1.这个CSRF 跨站请求伪造实际上就是利用你在 A网站的登录凭证进行获取浏览器历史记录从而发起攻击
2.CSRF 的条件 :
1. 用户已经有登录过要攻击的网站,并且有相应的用户信息如Cookie,heard 等相关信息。
2. 用户需要在其他网站没有关闭和用户信息过期前,打开 恶意网站 的指定连接。注意: 以上前提都最基础的,有可能可以在攻击之前的历史记录。
3.CSRF 的攻击原理 :
CSRF 实际上就是在用户访问了其他重要网站还没注销信息是,通过恶意网站吸引用户点击进来,通过一些列的脚本获取之前的浏览器记录等相关信息,并再次发送请求,从而实现攻击的效果。
他利用了服务器对浏览器的信任,因为浏览器默认请求会携带当前的Cookie等信息交给服务器的。csfr 攻击是通过 网站对浏览器的信任进行攻击,通过获取访问的历史记录进行攻击
解决方法就是添加了一个令牌参数,这令牌值是从服务器端生成,每次请求时都需要携带这个值,不然就会访问不成功!
令牌每次重新访问后都会重新生成!
CSRF 攻击的根源在于浏览器默认的身份验证机制(自动携带当前网站的 Cookie信息),这种机制虽然可以保证请求是来自用户的某个浏览器,但是无法保证这请求是用户授权发送。攻击者和用户发送的请求一模一样,这意味着我们没有办法去直接拒绝这里的某一个请求。如果能在合法请求中额外携带一个攻击者无法获取的参数。就可以成功分为两种不同的请求,进而直接拒绝掉恶意请求。在 SpringSecurity 中就提供了这种机制来防御 CSRF 攻击,这种机制我们称之为 令牌同步模式。
这是目前主流的 CSRF 攻击防御方案。具体的操作方式就是在每一个 HTTP 请求中,除了默认自动携带的 Cookie 参数之外,在提供一个安全的、随机生成的字符串,我们称之为 CSRF 令牌。这个 CSRF 令牌由服务端生成,生成后在 HttpSession 中保存一份。当前端请求到达后,将请求携带的 CSRF 令牌信息和服务端中保存的令牌进行比对,如果两者不相等,则拒绝掉该 HTTP 请求。
解决方法就是添加了一个令牌参数,这令牌值是从服务器端生成,每次请求时都需要携带这个值,不然就会访问不成功! 令牌每次重新访问后都会重新生成!
注意:考虑到会有一些外部站点链接到我们的网站,所以我们要求请求是幂等的,这样对子 HEAD、OPTIONS、TRACE 等方法就没有必要使用 CSRF 令牌了,强行使用可能会导致令牌泄露!
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public UserDetailsService userDetailsService (){
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("{noop}123").roles("admin").build());
return inMemoryUserDetailsManager ;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf(); // 开启 csrf 默认也是开启的
}
}
此时 Post 请求的会默认多一个隐藏属性 _csrf ,值会默认从HttpSession中获取到,请求时会默认携带该参数。

开启 CSRF 防御后会自动在提交的表单中加入如下代码,如果不能自动加入,需要在开启后手动加入如下代码,并随着请求提交。
web 请求会默认生成,默认保存在session作用域中。他会为所有非幂等的请求创建csrf,默认开启csrf 后是保存到session作用域中的。不符合前后端分离项目的 ,前后端分离系统,csrf令牌不再有服务器保存 ,是保存到cookie,但是不会被恶意攻击
获取服务端令牌方式如下:
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>

前后端分离开发时,只需要将生成 csrf 放入到 cookie 中,并在请求时获取 cookie 中令牌信息进行提交即可。
前后端分离系统,csrf令牌不再有服务器保存 ,是保存到cookie,但是不会被恶意攻击,因为传过来是需要放在heard中,并且还需要有一个key value 就是csrf的值,在spring Security 的Filter 会判断这个 key 和value 的格式和值是否相同 ,所以可以避免恶意攻击
1.修改 CSRF 存入 Cookie
设置 CSRF允许存放到Cookie 中,并且只允许Http 使用
核心代码就是
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())// 将令牌保存到cookie 中允许 cookie 被前端获取。
package com.bjpowernode.config;
import com.bjpowernode.filter.LoginJSONFilter;
import com.bjpowernode.hanlder.LoginFailureHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
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.AuthenticationException;
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.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author 千城丶Y
* @className : WebSecurityConfig
* @PACKAGE_NAME : com.bjpowernode.security.config
* @date : 2022/6/8 17:23
* @Description Security的配置
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final LoginFailureHandler loginFailureHandler ;
@Autowired
public WebSecurityConfig(LoginFailureHandler loginFailureHandler) {
this.loginFailureHandler = loginFailureHandler;
}
/**
* 配置数据源
* @return
*/
@Bean
@Override
public UserDetailsService userDetailsService (){
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
return userDetailsManager;
}
/**
* 配置自定义的数据源
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
/**
* 公共AuthenticationManager
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 创建自定义Filter
* @return
*/
@Bean
public LoginJSONFilter loginFilter() throws Exception {
LoginJSONFilter loginFilter = new LoginJSONFilter();
// 需要给自定义的Filter初始化
// 因为在配置时后面替换掉了之前的 UserNamePasswordAuthenticationFilter.所以配置都失效了
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setUsernameParameter("uname"); // 指定接收 json 用户名 key
loginFilter.setFilterProcessesUrl("/doLogin"); // 指定认证 url
loginFilter.setPasswordParameter("pwd"); // 指定接收 json 密码 key
// 认证失败处理
loginFilter.setAuthenticationFailureHandler(loginFailureHandler);
// 认证成功处理
loginFilter.setAuthenticationSuccessHandler(
(res,response,auth)->{
Map<String,Object> rs = new HashMap<>();
rs.put("code",200);
rs.put("msg","登录成功");
rs.put("authention",auth);
String s = new ObjectMapper().writeValueAsString(rs);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(s);
}
);
return loginFilter;
// loginFilter
}
/**
* 安全拦截认证
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/doLogin").permitAll() // 匿名访问
.anyRequest().authenticated() // 认证
.and()
// 当发送请求,还没有认证的时候,会抛出一个异常,所以返回json数据需要配合异常处理
// 就是当出现认证失败后是否要跳转到登录页面
.exceptionHandling() // 当认证失败的处理器
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
Map<String,String> rs = new HashMap<>();
rs.put("msg",authException.getMessage());
rs.put("error","未认证");
// 未认证状态
response.setStatus(HttpStatus.UNAUTHORIZED.value());
String json = new ObjectMapper().writeValueAsString(rs);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().println(json);
}
})
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.usernameParameter("uname")
.passwordParameter("pwd")
.failureHandler(loginFailureHandler)
.successHandler((res,response,auth)->{
Map<String,Object> rs = new HashMap<>();
rs.put("code",200);
rs.put("msg","登录成功");
rs.put("authention",auth);
String s = new ObjectMapper().writeValueAsString(rs);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(s);
})
.and()
.logout()
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/logout", HttpMethod.GET.name()),
new AntPathRequestMatcher("/logout",HttpMethod.POST.name())
))
.logoutSuccessHandler((res,resp,authentication)->{
Map<String,Object> rs = new HashMap<>();
rs.put("msg","退出登录成功");
rs.put("用户信息",authentication);
resp.setStatus(HttpStatus.OK.value());
String json = new ObjectMapper().writeValueAsString(rs);
resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
resp.getWriter().println(json);
})
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())// 将令牌保存到cookie 中允许 cookie 被前端获取。
.and();
// 替换掉这个Filter
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
2.测试

3. 发送请求携带令牌方式
key: _csrf // 这个可以在后端该不然不安全
value:"xxx"
X-XSRF-TOKEN : value // 这个key 也需要改不然不安全
如果开启了 CSRF 功能后,并设置允许生成cookie 也会到前端,那么每一次请求会生成cookie被写回。
注意:这个 CSRF 只适用于 Session 项目 ,如果使用 JWT TOKEN 则就不需要了,因为 jwt是保存在 heard 中而非cookie 。
首先可以知道核心的Filter 是 CsrfFilter 进行处理的令牌认证工作 。
通过以下源码可以查看到 CsrfFilter 里需要一个 tokenRepostroy 的类,这个类就是在配置里添加的,我们也可以自定义,通过这里可以得知,cookie里保存的名称是 _csrf ,heard 里的名称是 X-XSRF-TOKEN 和 requestParameter 的参数是 XSRF-TOKEN 的这些名称我们为了安全起见需要自定义替换掉的。

通过源码得知 Csrf 的核心处理类是 CsrfTokenRepository,它是一个接口

结论:
1.首先他会前去 cookie中拿cookie,如果没有则生成
2.然后回去请求头去获取,如果没有获取到则会去请求参数中获取
3.进行cookie的令牌和取出的令牌对比认证。
4.最终得出 请求参数和 请求头中只需要有一个有值就可以了
5.自定义值的话则直接修改这些属性就可以了。
6.正常令牌的验证没有通过 和服务器端的令牌进行比较,而是先从请求携带的cookie或者令牌,再去从请求或请求值中获取,进行比较。还是存在一定的安全问题。

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
// 1.从请求对象中去查询是否有对应的cookie 数据
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invalid CSRF token found for "
+ UrlUtils.buildFullRequestUrl(request));
}
if (missingToken) {
this.accessDeniedHandler.handle(request, response,
new MissingCsrfTokenException(actualToken));
}
else {
this.accessDeniedHandler.handle(request, response,
new InvalidCsrfTokenException(csrfToken, actualToken));
}
return;
}
filterChain.doFilter(request, response);
}
这个源码在CsrfConfigurer 里面定义一些 CsrfFilter 所需要的属性和配置信息。
核心方法 在里面创建了CsrfFilter
@Override
public void configure(H http) {
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
if (requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
}
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
if (accessDeniedHandler != null) {
filter.setAccessDeniedHandler(accessDeniedHandler);
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer
.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http
.getConfigurer(SessionManagementConfigurer.class);
if (sessionConfigurer != null) {
sessionConfigurer.addSessionAuthenticationStrategy(
getSessionAuthenticationStrategy());
}
filter = postProcess(filter);
http.addFilter(filter);
}
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva
所以我从维基百科上抓取了这段ruby代码并做了一些修改:@trie=Hash.new()defbuild(str)node=@triestr.each_char{|ch|cur=chprev_node=nodenode=node[cur]ifnode==nilprev_node[cur]=Hash.new()node=prev_node[cur]end}endbuild('dogs')puts@trie.inspect我首先在控制台irb上运行它,每次我输出node时,每次{}都会给我一个空哈希值,但当我实际调用时该函数使用参数'dogs'字符串构建,它确实有效,并输出{"d"=>
运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid
各位朋友们,大家好啊,今天我要分享的是关于文件操作方面的知识。文章目录为什么会有文件操作什么是文件文件操作文件指针文件的打开与关闭fopen(打开文件)fclose(关闭文件)打开文件的方式文件的顺序读写fgets函数fputc函数fgets函数fputs函数fprintf函数fscanf函数文件的非顺序读写fseek函数ftell函数rewind函数二进制读写fwrite函数`fread函数结语为什么会有文件操作那么大家可能会问:为什么会有文件操作呢?前面我们可能都了解了通讯录,我们知道当我们使用通讯录的时候我们可以添加联系人,也可以删除联系人,但是当我们退出程序之后下次再进来的时候,我们要
一、乱花迷人眼我就是被迷的那双眼。有时候需求来了,用熟悉的套路进行开发,确实很节省时间也能保证功能的稳定,但是这些开发的惯性无形中阻碍了我对技术的探索。我一直想改造详情页,解放重复功能开发的劳动力,但是详情页一眼望都是内容平铺,好像并没有什么可做的代码设计。后来我拨开繁花,发现详情页的组件化不必想的过于复杂,后台系统风格统一即可。因为大部分的详情页面是内容的展示,偶尔会出现少量的操作功能。将风格统一的部分进行组件化处理,操作功能使用回调函数放回当前页面,避免组件里做过多的业务逻辑。看,这不就成了。项目基于React框架开发的,所以代码写法是JSX语法,组件开发使用的hooks函数式组件,UI框
我正在开发一个简单的网站,让管理员提出问题并让用户解决问题。我在管理部分使用ActiveAdmin,在用户解决部分使用简单的AJAX调用。起初尝试通过ActiveAdmin::Devise登录是成功的,但无法退出。我删除了所有cookie,从那时起我无法在没有CSRFtoken真实性异常的情况下进行POST操作。我的application.html.erb头部有正确的meta_tags,声明为jquery_ujs(其他线程说这是一个常见问题),并且在两个POST操作中都存在真实性token。我什至尝试通过skip_before_filter:verify_authenticity_to
在我的Rails应用程序中,我在发送到api的ajax帖子中收到“警告:无法验证CSRFtoken真实性”。app/views/layouts/application.html.haml:!!!%html%head=stylesheet_link_tag"application",:media=>"all"=javascript_include_tag"application"=csrf_meta_tags%body=yieldajaxpost:$.ajax({url:'#{card_credits_path}',type:'POST',beforeSend:function(xhr)
我正在使用Sinatra框架在ruby中构建一个简单的应用程序。它主要基于“获取”——大多数请求将用于列出数据。但是,应用程序中有几个关键屏幕将收集用户输入。我想确保该应用尽可能安全,目前,我正在尝试寻找如何实现您在Rails表单中获得的那种真实性token?我要去的地方:好吧,我知道我需要csrf的token,但我不确定我是否需要自己生成它们,或者Sinatra是否可以为我生成它们-我已经查看了文档,他们说Sinatra正在使用机架保护,但是,我找不到它的任何示例代码,而且似乎无法弄清楚如何让它继续-任何帮助表示赞赏-谢谢! 最佳答案
我明白了OmniAuth::Strategies::OAuth2::CallbackErrorat/auth/google/callbackcsrf_detected|CSRFdetected我的代码:require'sinatra'require"sinatra/json"require"sinatra/config_file"require'omniauth-oauth2'require'omniauth-google-oauth2'useRack::Loggerconfig_file"config/app_config.yml"useRack::Session::Cookie,s