csrf(Cross-Site Request Forgery)-跨站请求伪造
攻击者诱导用户进入第三方网站中,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的用户认证凭证(在cookie中),完成冒充用户对被攻击的网站执行某项操作。
一个典型的CSRF攻击有着如下的流程:a.com是被攻击网站,b.com是第三方网站
- 受害者登录a.com,并保留了登录凭证(Cookie)。
- 攻击者引诱受害者访问了b.com。
- b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
- a.com以受害者的名义执行了act=xx。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
特点
- 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
- 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
- CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。
防护策略
1.双重Cookie验证(推荐)
请求头中加csrf token,加上cookie中的csrf_token进行验证。第一次的csrf token怎么来,登录的时候允许不使用csrf token,然后登录请求调用后会返回一个csrf token 让其设置在cookie中
Spring security中的**CookieCsrfTokenRepository**
就是使用该方式
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单
// http.authorizeRequests().anyRequest().authenticated().and().formLogin();
// http.sessionManagement().sessionFixation().migrateSession().maximumSessions(1).maxSessionsPreventsLogin(true);
//自定义认证方式
//csrf设置 使用双重cookie校验
CookieCsrfTokenRepository cookieCsrfTokenRepository = new CookieCsrfTokenRepository();
cookieCsrfTokenRepository.setCookiePath("/");
cookieCsrfTokenRepository.setCookieHttpOnly(false);
http.csrf().ignoringAntMatchers("/login").csrfTokenRepository(cookieCsrfTokenRepository);
//停用form表单登录
http.formLogin().disable();
//防御点击劫持 如果有html页面
http.headers().frameOptions().disable();
//自定义认证
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//url授权设置
http.authorizeHttpRequests().antMatchers("/login").permitAll().anyRequest().authenticated();
// 认证 授权失败设置
http.exceptionHandling().accessDeniedHandler(accessDeniedHandlerConfig).authenticationEntryPoint(authenticationHandlerConfig);
}
缺点:
- 如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效。
- 难以做到子域名的隔离。
- 如果用户访问的网站为
www.a.com
,而后端的api域名为api.a.com
。那么在www.a.com
下,前端拿不到api.a.com
的Cookie,也就无法完成双重Cookie认证。 - 于是这个认证Cookie的domain属性必须设置
.a.com
下,这样每个子域都可以访问。 - 任何一个子域都可以修改
a.com
下的Cookie。 - 某个子域名存在漏洞被XSS攻击(例如
upload.a.com
)。虽然这个子域下并没有什么值得窃取的信息。但攻击者修改了a.com
下的Cookie。 - 攻击者可以直接使用自己配置的Cookie,对XSS中招的用户再向
www.a.com
下,发起CSRF攻击。
- 如果用户访问的网站为
2.使用cookie的same site属性
防止CSRF攻击的办法已经有上面的预防措施。为了从源头上解决这个问题,Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax
如果SamesiteCookie被设置为Strict,浏览器在任何跨域请求中都不会携带Cookie,新标签重新打开也不携带,所以说CSRF攻击基本没有机会。
但是跳转子域名或者是新标签重新打开刚登陆的网站,之前的Cookie都不会存在。尤其是有登录的网站,那么我们新打开一个标签进入,或者跳转到子域名的网站,都需要重新登录。对于用户来讲,可能体验不会很好。
如果SamesiteCookie被设置为Lax,那么其他网站通过页面跳转过来的时候可以使用Cookie,可以保障外域连接打开页面时用户的登录状态。但相应的,其安全性也比较低。
另外一个问题是Samesite的兼容性不是很好,现阶段除了从新版Chrome和Firefox支持以外,Safari以及iOS Safari都还不支持,现阶段看来暂时还不能普及。
而且,SamesiteCookie目前有一个致命的缺陷:不支持子域。例如,种在topic.a.com下的Cookie,并不能使用a.com下种植的SamesiteCookie。这就导致了当我们网站有多个子域名时,不能使用SamesiteCookie在主域名存储用户登录信息。每个子域名都需要用户重新登录一次。
总之,SamesiteCookie是一个可能替代同源验证的方案,但目前还并不成熟,其应用场景有待观望。
3.csrf token方式
通过服务端生成token,给前端,前端请求时,请求头需要加上csrf token键值对
- 将CSRF Token输出到页面中,前后端不分离使用
- 页面提交的请求携带这个Token
- 服务器验证Token是否正确
参考文档
- https://tech.meituan.com/2018/10/11/fe-security-csrf.html
- https://yiyige.blog.csdn.net/article/details/121032202
xss(cross site script)-跨站脚本攻击
攻击者在页面中插入恶意的javascript代码,当用户浏览网页的时候,嵌入在web里面的javascript代码会执行,进而达到恶意的目的
防护措施
原则
防护XSS攻击其实就记住两条原则:过滤输入和转义输出
- 在输入方面对所有用户提交内容进行可靠的输入验证,提交内容包括URL、查询关键字、http头、post数据等;
- 在输出方面,在用户输内容中使用标签,标签内的内容不会解释,直接显示;
- 严格执行字符输入字数控制;
- 在脚本执行区中,应绝无用户输入。
浏览器的 X-XSS-Protection
浏览器内置了一个针对XSS攻击的防护机制,这个浏览器内置的防护机制就是所谓的XSS filter,这个防护机制主要用于减轻反射型XSS 攻击带来的危害
至于怎么设置浏览器的XSS防护机制,其实很简单,只要在HTTP响应报文的头部增加一个X-XSS-Protection 字段,明确地告诉浏览器XSS filter/auditor该如何工作。
X-XSS-Protection 的字段有三个可选配置值
- 0: 表示关闭浏览器的XSS防护机制
- 1:删除检测到的恶意代码, 如果响应报文中没有看到X-XSS-Protection 字段,那么浏览器就认为X-XSS-Protection配置为1,这是浏览器的默认设置
- 1; mode=block:如果检测到恶意代码,在不渲染恶意代码(推荐)
X-XSS-Protection (Headers) - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云
参考文档
- https://yiyige.blog.csdn.net/article/details/121032202
- https://blog.csdn.net/zhongguowangzhan/article/details/104044178
会话固定攻击
会话固定攻击的根源,即在于会话的 sessionid 不变!如果用户在未登录时拿到的是一个 sessionid,登录之后服务端给用户重新换了一个新的 sessionid,就可以防止会话固定攻击了。
防御措施
更改session
在spring security中,提供了三种防御策略
- none: 该策略对会话不做任何变动,登录之后会沿用旧的session;(没有起到防护)
- migrateSession: 默认策略,用户登录后创建一个新的session,并将旧session中的数据复制过来;(对应实现类
**SessionFixationProtectionStrategy**
) - newSession: 用户登录后会创建一个新的session;(对应实现类
**SessionFixationProtectionStrategy**
)newSession: 用户登录后会创建一个新的session;(对应实现类**SessionFixationProtectionStrategy**
)
- changeSessionId: 表示 session 不变,不会创建新的session,但是会修改 sessionid,内部使用由Servlet容器提供的会话固定保护。(对应实现类
**ChangeSessionIdAuthenticationStrategy**
)
参考
- url重写:https://www.cnblogs.com/wonderKK/p/3393490.html
- https://yiyige.blog.csdn.net/article/details/120716322
点击劫持
原理
点击劫持是视觉欺骗,用户只看到了底层页面,与页面进行交互时却是与上层页面在交互。这是由于透明的iframe造成的,通过控制iframe的位置,导致上层页面的按钮等覆盖到下层上。
注:红色按钮(上层页面中的)应该是完全覆盖在黄色按钮(底层页面中的),由于看的不是很清楚,我稍微错位了一下。
示例
<html>
<head>
<title>
来和鹿鸣Lumi互动吧!!!
</title>
<head>
<style>
iframe {
width: 1440px;
height: 900px;
position: absolute;
top: 30px;
left: 450px;
z-index: 2;
-moz-opacity: 0;
opacity: 0;
filter: alpha(opacity=0);
}
</style>
</head>
</head>
<body>
<center>
<div>
<img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1242777858,3501957407&fm=11&gp=0.jpg" height="60%">
<br>
<button>点击进行互动</button>
</div>
</center>
<iframe src="https://blog.csdn.net/lady_killer9" scrolling="no"></iframe>
</body>
</html>
opacity为0.5,即上层页面为半透明状态。
可以看到关注按钮覆盖在了下方的点击进行互动按钮上。
opacity为0时,此时,上层页面完全无法看见,这时如果你登录csdn后点击"点击进行互动"按钮,就会关注我
防御
前端
一般是通过禁止跨域的iframe来防范,X-Frame-Options
HTTP响应头是用来确认是否浏览器可以在frame或iframe标签中渲染一个页面,网站可以用这个头来保证他们的内容不会被嵌入到其它网站中,以来避免点击劫持。
<meta http-equiv="X-FRAME-OPTIONS" content="DENY">
**TIP:在实际项目中使用了,但不起作用,还是推荐通过后端Nginx配置来禁止iframe嵌套 **
DENY:表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
SAMEORIGIN:表示该页面可以在相同域名页面的 frame 中展示。
ALLOW-FROM uri:表示该页面可以在指定来源的 frame 中展示。
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://example.com/
X-Frame-Options (Headers) - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云
服务器端
nginx
在配置文件的 http 、server 或者 location 中加入如下代码,来防止被嵌套。
add_header X-Frame-Options "SAMEORIGIN";
参考文档
- https://blog.csdn.net/qq_31851435/article/details/128369756
- https://blog.csdn.net/lady_killer9/article/details/108017437
计时攻击
计时攻击是旁路攻击的一种,在密码学中,旁道攻击又称侧信道攻击、边信道攻击
计时攻击(Timing attack),通过设备运算的用时来推断出所使用的运算操作,或者通过对比运算的时间推定数据位于哪个存储设备,或者利用通信的时间差进行数据窃取。
spring security中耗时的地方主要是 不同加密算法后的 用户输入明文密码和数据库中加密密码比对
1.spring security使用下面的代码进行防御计时攻击
2.首先方法一进来调用了 prepareTimingAttackProtection 方法,从方法名字上可以看出,这个是为计时攻击的防御做准备
- 接下来调用 loadUserByUsername 方法,根据登录用户传入的用户名去数据库中查询用户,如果查到了,就将查到的对象返回。
如果查询过程中抛出 UsernameNotFoundException 异常,按理说直接抛出异常,接下来的密码比对也不用做了,因为根据用户名都没查到用户,这次登录肯定是失败的,没有必要进行密码比对操作!但是为了防御计时攻击。springsecurity会进行密码比对
这里首先获取到登录用户传入的密码即 presentedPassword,然后调用 passwordEncoder.matches 方法进行密码比对操作,本来该方法的第二个参数是数据库查询出来的用户密码,现在数据库中没有查到用户,所以第二个参数用 userNotFoundEncodedPassword 代替了,userNotFoundEncodedPassword 就是我们一开始调用 prepareTimingAttackProtection 方法时赋值的变量。这个密码比对,从一开始就注定了肯定会失败