Web安全:跨站点攻击csrf

什么是CSRF?

你可以理解为:攻击者盗用你的身份,以你的名义发送恶意请求。它被称为“跨站请求伪造”。它能干的事情有很多:当你打开某个网站时,你另一个已经打开的购物网站已经完成支付;以你名义发送邮件,发评论;网络蠕虫;虚拟货币转账…

CSRF攻击流程

  • 用户登录A网站(受信任的)
  • A网站确认身份
  • 在A中访问B网站链接(危险的)
  • B网站拿到A中的 cookie 后直接向其服务器发送请求

这看似简单,其中却有一些值得注意的问题。比如:
1. B网站向A服务器发送请求,是否会引起跨域问题?

不会。因为并不是所有的请求都会引起跨域。如 HTML 中的 <img><script> 标签就不会。而且它们的 src 中放的链接其实就是一次 get请求。

著名的jsonp就是用了 script 允许跨域的特性实现的

而且,通过 form 表单发送POST请求的方式也会被用来做攻击。笔者曾看过这样的一段代码:

<? php
	session_start();
	if(isset($_POST['toBankId']) && isset($_POST['money'])){
		buy_stocks($_POST['toBankId'],$_POST['money']);
	}
?>
# 服务端为了防止由img导致的get请求轻易地获取数据,改为只接收POST请求,但
<body onload="steal()">
	<iframe name="steal" display="none">
		<form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php" target="steal">
			<input type="hidden" name="toBankId" value="11" />
			<input type="hidden" name="money" value="1000" />
		</form>
	</iframe>
	
	<script>
		function steal(){
			iframe=document.frames["steal"];
			iframe.document.submit("transfer");
		}
	</script>
</body>

隐藏的 iframe 表单提交 —— 如果一个 form 表单的 target 值等于 iframename 值。那么这个表单数据会在 iframe 中提交,进而跳转。此时,如果这个 iframe 还设置了 display:none; 那么在页面看来并没有什么“不适”。

2. 攻击者能拿到cookie吗?

不能。CSRF攻击是攻击者利用 cookie 欺骗服务器,但攻击者并不能知道 cookie 的内容。而且,对于服务器返回的数据(结果),由于浏览器同源的限制,攻击者也无法解析。他所做的也就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接修改数据的值。而非窃取数据!

3. 上面说要csrf生效,基本需满足两点:1、登录受信任网站A,并在本地生成Cookie;2、在不登出A的情况下,访问危险网站B。那如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击?

理论上确实如此,但你不能保证以下情况不会发生:

  1. 你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
  2. 你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了…)
  3. 所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

CSRF的防御

1. 前端token,后端校验
CSRF 攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。
要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造或者拿不到的信息;或者可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并将token保存到本地(cookie中),然后在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

简单来说就是:在服务端生成一个随机的token并保存起来并且跟随请求带给前端。而前端拿到后保存到一个隐藏域中,跟随每次请求带到后端:

// 后端代码,这里以node(koa)为例
let csrfToken=parseInt(Math.random()*9999999,10);

ctx.cookies.set('csrfToken',csrfToken);
<input calss="csrfToken" type="hidden" name="csrfToken" value="" />
<script>
document.querySelector(".csrfToken").value=getMyCookie('csrfToken');
function getMyCookie(key){
	var val = "";

	// 对cookie操作
	var cookies = document.cookie;
	cookies = cookies.replace(/\s/,"");
	var cookie_array = cookies.split(";");
	for(i=0;i<cookie_array.length;i++){
		// name=mxc
		var cookie = cookie_array[i];
		var array = cookie.split("=");
		if(array[0]==key){
			val = array[1];
		}
	}
	
	return val;
}
</script>

在每次请求的回调中都生成一个新的值,然后在下一次请求过来时校验(cookie中的值和参数中的值相同,并且和后端生成的值也相等)。

这样就要求必须同时知道参数中的token和cookie中的token,才能成功发动攻击

2. same-site属性
cookie自带的有一个sami-site属性,可以控制第三方网站是否携带cookies:

  • Strict:任何请求都不允许带cookies
  • Lax(默认):允许比如链接可以携带cookies
  • None

写法:

// 以koa为例
ctx.cookies.set(
	'cid',    //第一个参数,key
	'hello world',   //第二个参数,value
	{   //第三个参数,cookie设置
		domain: 'localhost',  // 写cookie所在的域名
		path: '/index',       // 写cookie所在的路径
		maxAge: 10 * 60 * 1000, // cookie有效时长(http1.1)
		expires: new Date('2017-02-15'),  // cookie失效时间(http1.0)
		httpOnly: false,  // 是否只用于http请求中获取
		sameSite: Strict,
		overwrite: false  // 是否允许重写
	}
)

但是这种方式目前还存在兼容性问题,而且受支持程度并不好。

3. 访问来源
其实简单的还有一种方式:通过 document.referrer 判断当前页面是从哪个链接过来的。通常也会随header携带到服务端,服务端也可以获取到:req.headers.referrer
但是referrer可以被伪造,这种方式非常不安全。


当然,还可以通过如Spring中的某些设置或者一些方式去达到防御的目的。笔者在查阅资料途中发现了一篇文章,手段比较偏后端的。感兴趣的可以点我移步这里查看

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恪愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值