CSRF攻击与XSS攻击,怎么防御
CSRF攻击
前言
CSRF与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。
XSS是通过插入恶意的攻击脚本到用户信任的网站,代码被前端解析后,请求服务器。
CSRF是伪装成用户信任的网站发起请求到服务器。
CSRF攻击不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性
基于Session方式
Csrf 伪造登录网址盗取用户密码(Form表单方式的csrf)
详细:黑客发送伪造的链接给用户点击,用户点击后,以为是真正的网站,所以进行登录操作,输入了用户名和密码。
具体模拟:
1、首先真正的网站有个登录校验的POST请求,例如: POST http://www.xxxx.com/login 这里是登录校验请求,传参格式为Form表单。
2、新建一个html文件,包含登录功能,但登录校验功能使用真正的网站的请求 。
3、黑客把html文件做成一个网站,然后发送网站路径给A用户
4、A用户点击后,就会跳转到伪造网站,接着让用户输入用户名和密码,发送给真正的网站校验。
5、这样的后果是,导致用户在不知情的情况下泄露了用户名和密码。这是不可知的操作,是很危险的。
伪造测试页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>csrf 测试攻击类</title>
</head>
<body>
<h1>伪造登录界面</h1>
<form action="http://127.0.0.1:8091/login" method="post">
<div>
<!--name必须为“username"-->
<input type="text" name="username" placeholder="用户名"/>
</div>
<div>
<!--name必须为"password"-->
<input type="password" name="password" placeholder="密码"/>
</div>
<input type="submit" value="登录"/>
</form>
</body>
</html>
解决办法:
方案一、
- 添加
限制跨域访问
,非允许的网站的资源不可访问,这样就可以限制了,在伪造网址里使用 js方式调用请求。 使用csrf令牌
,对Form表单进一步保护。因为第三方网站可以通过form表单提交,跳过 js/限制跨域访问
方式,来请求,所以我们需要一个表单参数csrf令牌,来防御csrf攻击。具体在所有form表单里添加 csrf令牌 表单参数,然后后台验证csrf令牌是否合法,这个csrf令牌是由服务端生成的。
方案二、
- 添加
限制跨域访问
- 我们也可以使用
Referer验证
的方式,来防御csrf攻击。
csrf 伪造网址,让用户在不知情的情况下新增数据。(直接网址拼接方式的csrf)
详细:黑客发送伪造的链接给用户点击,用户点击后,后台新增了一条数据。
具体模拟:
1、首先真正的网站有个新增的GET请求,例如:GET http://www.xxxx.com/order/add?details=1,2,3,4 这里的意思是,把这几个商品加入到订单里。
2、新建一个html文件,包含内容 <script>location.href=“http://www.xxxx.com/order/add?details=1,2,3,4”</script>
3、黑客把html文件做成一个网站,然后发送网站路径给A用户
4、A用户点击后,就会跳转到伪造网站,接着伪造网站就会把参数信息带上,发送给真正的网站。
5、这样的后果是,导致用户在不知情的情况下新增了订单。这是不可知的操作,是很危险的。
解决办法:
方案一、
- 避免使用GET请求做一些操作数据库的动作。
- 添加
限制跨域访问
使用csrf令牌
,对Form表单进一步保护,防止伪造网站使用 Form表单方式跳转页面。尽量少用Form表单
,Form表单方式请求,可以越过跨域访问,导致第三方可以使用Form表单,所以Form表单不安全。若用Form表单,则都需要添加csrf令牌。
方案二、
- 避免使用GET请求做一些操作数据库的动作。
- 添加
限制跨域
访问 - 使用
Referer验证
的方式,来防御csrf攻击。
基于Jwt方式
为什么Jwt没有csrf问题
因为Jwt是 基于js的方式请求的,所以我们只需要添加限制跨域访问
,就可以预防csrf问题。
jwt也不存在Form表单问题。Jwt不走“在游览器直接访问”这条路的,所有Form表单都是在js完成的。js又被限制跨域访问
所限制,只能访问允许的。
jwt:若黑客故意走“在游览器直接访问”这条路,这时,黑客就需要知道token了,因为jwt不会像session一样,用户点击链接,游览器自动帮用户加上jsessionId。黑客需要手动添加token信息,但黑客不知道,所以使用jwt安全。
jwt就像csrf令牌,自带验证。
术语
跨域访问限制
安全系数:★★★★★
跨域访问限制可以阻止第三方使用js调用我们网站请求
CSRF令牌
安全系数:★★★★★
CSRF令牌
:为每个用户会话生成唯一的CSRF令牌,并将该令牌添加到所有请求中。这样,即使攻击者能够窃取用户的会话ID,但由于缺乏正确的CSRF令牌,他们也无法成功执行CSRF攻击。
Referer验证
安全系数:★★★★
Referer SDK
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
logger.debug("拦截器拦截到对:{}的访问", request.getRequestURI());
String referer = request.getHeader("referer");
logger.debug("referer:{}", referer);
StringBuilder sb = new StringBuilder();
sb.append(request.getScheme()).append("://").append(request.getServerName());
logger.debug("basePath:{}", sb.toString());
if (referer == null || referer == "" || !referer.startsWith(sb.toString())) {
response.setContentType("text/plain; charset=utf-8");
response.getWriter().write("非法访问,请通过页面正常问!");
return false;
}
return true;
}
参考文章:https://blog.csdn.net/qq_37432174/article/details/128001627
疑问答辩
Form表单提交为什么会有csrf攻击问题
由于第三方网站可以使用Form表单提交方式向网站发送请求,并且没有跨域问题。这样第三方就可以利用这个漏洞请求我们网站,进行操作。
扩展:
为什么form表单提交没有跨域问题,但ajax提交有跨域问题?
这里有个例子:
- 页面Form表单提交
这个没有跨域问题
<form action="http://127.0.0.1:8091/login" method="post">
<div>
<!--name必须为“username"-->
<input type="text" name="username" placeholder="用户名"/>
</div>
<div>
<!--name必须为"password"-->
<input type="password" name="password" placeholder="密码"/>
</div>
<input type="submit" value="登录"/>
</form>
警告
:我觉得是这个Form元素有问题,但凡是用Form,都能跳过跨域拦截。Form支持的传输格式有application/x-www-form-urlencoded、multipart/form-data、text/plain。所以你们要注意点这几个请求方式,都存在CSRF问题,跨域是解决不了的,需要使用csrf令牌或者Referer解决。
- js Form表单提交
这个有跨域问题
<button id="test2">测试js方式,提交Form表单</button>
document.getElementById('test2').onclick = function(){
const formData = new FormData();
formData.set('username','admin')
formData.set('password','admin2')
axios.post('http://127.0.0.1:8091/login',formData,{headers: {
'Content-Type': 'multipart/form-data'
}}).then(res=>{
console.log(res)
})
}
参考文章:为什么form表单提交没有跨域问题,但ajax提交有跨域问题?
结论
-
跨域访问限制可以解决90%的问题。
-
剩下10%,是GET请求和Form表单提交导致的,这两个留了一个有漏洞的口子。
- GET请求:所以我们GET请求只用来做查询操作,防御CSRF攻击
- Form表单:“Form表单提交”支持这三种格式,所以这三种才会有漏洞:application/x-www-form-urlencoded、multipart/form-data、text/plain
解决方法一: Form表单使用csrf令牌,防御CSRF攻击。
解决方法二: Form表单使用Referer验证,防御CSRF攻击
注意:因为我们使用Form表单请求都是添加或修改操作,建议使用POST请求
XSS攻击
前言
CSRF与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。
XSS是通过插入恶意的攻击脚本到用户信任的网站,代码被前端解析后,请求服务器。
CSRF是伪装成用户信任的网站发起请求到服务器。
CSRF攻击不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性
XSS
跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。在不同场景下,XSS有相应不同的表现形式,主要分为反射型
、存储型
以及DOM型
的跨站脚本攻击,所造成的影响主要是窃取用户登录凭证(Cookies)、挂马攻击、页面访问挟持等。
类型
存储型
这种攻击多见于论坛、博客和留言板,攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入帖子的内容中。随着帖子被服务器存储下来,恶意脚本也永久地被存放在服务器的后端存储器中。当其他用户浏览这个被注入了恶意
通过保存html元素到数据库字段里,字段在页面展示时,没有对字段进行编码,导致游览器以为是html元素,然后直接解析元素,导致数据库字段里的html元素可以操纵页面。
解决办法:
- 展示方面优化:
- 对数据库字段里的html元素进行编码后在展示。
- 使用安全的API:在修改DOM时,使用安全的API来操作DOM元素。避免使用innerHTML等易受攻击的API,而是使用textContent或setAttribute等更安全的方法。
- 保存字段方面优化:过滤脚本元素
反射型
攻击者通过电子邮件等方式将包含XSS代码的恶意链接发送给目标用户。当目标用户访问该链接时,服务器接收该目标用户的请求并进行处理,然后服务器把带有XSS代码的数据发送给目标用户的浏览器,浏览器解析这段带有XSS代码的恶意脚本后,就会触发XSS漏洞
是通过URL参数直接注入,一般是使用alert来探测站点是否防御,直接攻击的使用src来引入自己的脚本
例如:
- 在参数中注入script脚本
http://localhost:1521/?from=<script>alert(1)</script>bing
- 页面访问时直接使用参数,导致脚本执行
<div>${from}</div>
解决办法:
- 展示方面优化:
- 对数据库字段里的html元素进行编码后在展示。
- 使用安全的API:在修改DOM时,使用安全的API来操作DOM元素。避免使用innerHTML等易受攻击的API,而是使用textContent或setAttribute等更安全的方法。
- 传参参数字段方面优化:过滤脚本元素
- 使用Content Security Policy(CSP 内容安全策略),限制主动访问外部资源(例如限制引入外部第三方 js)
DOM型
攻击者通过修改网页的DOM结构来实施攻击,当用户浏览网页时,恶意脚本会被执行。
DOM型xss和别的xss最大的区别就是,反射型是打开就会执行,DOM型是用户点击该链接时触发。
反射型就像一个是导弹,到达目的地就爆炸,另一个DOM型就像地雷,等待人去踩。
模拟注入DOM对象,点击DOM对象,就会执行一系列操作。
例如:
- 这里点击DOM就会跳转新页面。
<body>
<!-- value = <a href="https://blog.csdn.net/" target="_blank">点击</a> -->
<input type="text" name="node" value="" style="width:300px;"> <button id="btn1">添加</button>
<div>
<div>输入值:<div id="content"></div></div>
</div>
<script>
document.getElementById('btn1').onclick = function(){
let input = document.querySelector('input[name="node"]')
document.getElementById('content').innerHTML = input.value
}
</script>
- 这里下方有个点击按钮,点击就会跳转新页面。
注意:他们可能还会注入第三方的脚本文件
解决方法:
- 展示方面优化:
- 对数据库字段里的html元素进行编码后在展示。
- 使用安全的API:在修改DOM时,使用安全的API来操作DOM元素。避免使用innerHTML等易受攻击的API,而是使用textContent或setAttribute等更安全的方法。
- 传参参数字段方面优化:过滤脚本元素
- 使用Content Security Policy(CSP 内容安全策略),限制主动访问外部资源(例如限制引入外部第三方 js)
总结
最终解决办法:
展示方面优化
:- 对数据库字段里的html元素进行编码后在展示。
- 使用安全的API:在修改DOM时,使用安全的API来操作DOM元素。避免使用innerHTML等易受攻击的API,而是使用textContent或setAttribute等更安全的方法。
保存字段方面优化
:过滤脚本元素传参参数字段方面优化
:过滤脚本元素使用Content Security Policy(CSP 内容安全策略)
,限制主动访问外部资源(例如限制引入外部第三方 js)
具体可实现解决:
展示方面优化
- 前端一般使用vue等框架,所以,它们已经帮我们处理好了,我们不用管。
使用Content Security Policy(CSP 内容安全策略)
- spring-security 提供了csp sdk,我们只需要配置一下就好了。
剩余的:
在特定情况下,可以忽略,例如使用vue等框架展示字段。
保存字段方面优化
:过滤脚本元素- 这个可以忽略,只要展示方面做好优化,就不会出问题了。
传参参数字段方面优化
:过滤脚本元素- 传参字段不能直接解析到页面,现在都前后端分离了,前端都使用vue等框架,传参等字段都会先通过vue等框架展示,所以也不需要我们去处理。
spring-security CSP SDK
参考文章:https://springdoc.cn/spring-security-csp/
@Configuration
public class ContentSecurityPolicySecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("form-action 'self'");
return http.build();
}
}