CSRF介绍
CSRF(跨站请求伪造)
Cross-site request forgery 简称为“CSRF”,在CSRF的攻击场景中攻击者会伪造一个请求(这个请求一般是一个链接),然后欺骗目标用户进行点击,用户一旦点击了这个请求,整个攻击就完成了。
实际场景举例
小明在tao.com网上,登录个人账号,修改本人信息(修改信息本质就是发送一个http://www.tao.com/edit?address=xiamen)。
攻击者知道了修改他人信息的请求是上面这个连接。
攻击者做一个网站(或者一个链接),利用社会工程学或者其他一些手段骗小明点击这个链接或者图片之类的,小明的个人信息就被修改了。
能成功的关键点在于:
1.tao.com网站本身在修改个人信息没有加校验,导致这个请求很容易被伪造。
2.小明本身已经登录过了tao.com网站,cookies中已经存储登录后的token,所以当点击链接就会导致,像是小明自己发送请求要修改个人信息。
CSRF是借用户的权限完成攻击
解决方案
网站本身要在敏感操作的时候加校验
尽量使用post请求
- Referrer
HTTP头中的Referer字段记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,
而如果黑客要对其实施 CSRF攻击,他一般只能在他自己的网站构造请求。因此,可以通过验证Referer值来防御CSRF 攻击。
Referrer会被伪造或者篡改
- 浏览器插件修改Referrer
- 通过网关或者抓包修改 Referrer。(这是中间人攻击,也不属于 CSRF 攻击范畴。防中间人攻击用 HTTPS。)
- 验证码
敏感操作加上验证码(短信通知之类),后端判断验证码就可以防御CSRF。过多验证操作对用户使用不太友好。 - hash
当要放问修改页面时候,生成hash值,且不保存在cookies当中。当执行修改信息操作要携带hash值才能修改成功。
具体解决方案可以使用SpringSecurity(默认就是开启csrf,我这里关闭了)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
....
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);