CSRF 是啥?
CSRF 是指跨站请求伪造,是 Cross-site request forgery 的简称,有些地方也简写为 XSRF。简而言之,就是攻击者通过某种手段,让用户访问曾经认证过的网站并执行特定操作,因为用户之前已经认证过了,因此这个操作会被误认为是用户执行的操作。
举个例子,假如一个银行的转账接口地址是 https://bank.com/withdraw?account=xxx&amount=1000
,一个恶意网站就可以在页面上放置如下代码:
<img src="https://bank.com/withdraw?account=xxx&amount=1000" />
如果用户刚刚登陆过了银行的网站,并且有访问了恶意站点,那么,由于登录信息未过期,他将在不知情的情况下转出一笔钱。
由此可以看出,CSRF 攻击利用的是网站服务器对浏览器的信任,简单的身份认证只能确保请求发自用户的浏览器,却不能保证请求是用户资源发出的。
如何通过 Spring Security 防御 CSRF 攻击?
在项目中引入 Spring Security 之后,就会自动开启 Spring Security 的 CSRF 防御,可以在配置中关闭这个特性,如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
/* 其他配置 */
http.csrf().disable();
}
在不手动关闭 CSRF 防御的情况下,Spring 会在前端发送 POST/PUT/PATCH/DELETE 请求的时候进行 CSRF 校验,校验的方式就是在这些请求的时候,需要带上一个参数。无论你使用的是 Thymeleaf 还是 JSP 页面模版,都需要在表单中添加一个隐藏域:
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
这个隐藏域的参数名和值,会在渲染页面的时候渲染出来,并在提交表单的时候,自动进行校验。这里的参数名是 _csrf
,值是一个随机的字符串,这样通过校验后,就能保证请求是在这个页面提交的,因为通过攻击提交的请求不会携带这个参数,或者没办法传递正确的值。
那如果是前后端分离的架构方式呢?Spring Security 也提供了方案。可以通过一下配置来完成。
@Override
protected void configure(HttpSecurity http) throws Exception {
/* 其他配置 */
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
这时,Spring Security 会把校验的 token 值,放在 Cookie 中返回前端,我们的前端页面,需要从 Cookie 中获取到 token,然后,在发送请求的时候,将其作为请求参数 _csrf
的值,传递到后端。
由于浏览器安全策略的原因,之后我们的网站前端能够获取到 Cookie 中的内容,恶意网站是无法获取到 Cookie 中的内容的。
还有哪些办法?
除了校验一个随机的 token 内容,还有一个方法就是校验 Referer,Referer 是 Header 中的一个字段,表示请求的来源。我们可以根据这个内容,来获取请求的来源是不是用户界面所在的页面,如果不是,则拒绝处理请求。
除此之外,作为开发者,对于敏感操作,产品设计人员和开发者应该有足够的安全意识,比如,银行转账操作,一定不能通过一个简单的请求就处理,至少需要更多的安全校验,比如交易密码、短信验证码、人脸或指纹认证等。