CSRF原理详解
一、简述
CSRF,Cross Sites Request Forgery,跨站请求伪造,发生在两个网站之间。一个网站是黑客的钓鱼网站,一个是受害者在浏览器上已登录的具有会话cookie的网站
简单例子
假如一家银行用以执行转账操作的URL地址如下 https://bank.example.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放上<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman" />
如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险
透过例子能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户的浏览器,让其以用户的名义执行操作
二、以具体实验分析原理
这里我们以一个实验分析CSRF的原理
1.书店系统搭建
首先在192.168.163.103上基于nginx搭建一个BookstoreSystem
项目地址https://github.com/pasiphae321/BookstoreSystem
在192.168.163.101上基于nginx搭建一个钓鱼网站,其上有个网页test.html内容如下
在192.168.163.1(后文省略为1,假设为受害者主机)上,修改本地的DNS文件,win11的路径为C:\Windows\System32\drivers\etc\hosts
在1的浏览器上访问http://test.pasiphae.net/BookstoreSystem/login.php,以what/cccccc进行登录

成功登录进去了
2.Cookie的SameSite属性
Cookie的SameSite属性规定是否在跨站请求中携带该Cookie,SameSite属性有三个值
- Strict:所有的跨站请求都不携带Cookie,那么CSRF攻击将不存在
- Lax:部分跨站请求携带Cookie
- None:所有跨站请求都携带Cookie,不过此时需满足Secure属性为true,即该请求为HTTPS请求
此时还要注意跨源和跨站的区别,可以参考我的文章跨源与跨站、CORS、Cookie的SameSite属性浅析
Lax
因为此时Cookie的SameSite属性为Lax,那么只有下图中的前三项会在跨站请求中携带Cookie
超链接
此时,受害者没有关闭浏览器,其test.pasiphae.net的会话cookie还未消失。他此时访问了www.pasiphae.com/test.html,点击了领取礼包的超链接。虽然此时属于跨站,同时属于跨源,但跨站的Lax如上图所示在超链接情况下会携带cookie,跨源但属于简单跨源请求,不会进行CORS预检
那么,携带会话cookie的请求就会被发送,what用户的密码就会被从cccccc修改为1894787

可以看到,what的密码确实被修改为了1894787
GET表单
将www.pasiphae.com/test.html更改成一个GET表单

重新登录what
受害者填写了收货信息并点击提交
此时的跨站请求也携带了会话Cookie,密码被修改

POST表单
将www.pasiphae.com/test.html更改成一个POST表单
重新登录what
受害者填写收获信息并点击提交
POST表单确实不会携带上Cookie,这和上面的那个表格相吻合
Strict
我们在test.pasiphae.net的后端,将SameSite属性设置为Strict,那么此时点击链接发送的请求不会再包含cookie,下面我们进行验证
在www.neptune.com上将test.html修改下
我们对此进行验证
重新登录what
会话cookie跟刚才肯定是不一样的
这次就没有携带cookie了,所以也就修改不了密码

同样,GET表单和POST表单也不会携带cookie,这里就不作验证了
None
因为None需要结合https使用,这里暂时不做验证
三、防范CSRF攻击措施
1.令牌同步模式
令牌同步模式(Synchronizer token pattern,简称STP)
原理是,当用户发送请求时,服务器端应用将令牌(token,一个保密且唯一的值)嵌入HTML表格,并发送给客户端。客户端提交HTML表格时候,会将令牌发送到服务端,令牌的验证是由服务端实行的。令牌可以通过任何方式生成,只要确保随机性和唯一性(如:使用随机种子的哈希链 )。这样确保攻击者发送请求时候,由于没有该令牌而无法通过验证
STP能在HTML下运作顺利,但会导致服务端的复杂度升高,复杂度源于令牌的生成和验证。因为令牌是唯一且随机,如果每个表格都使用一个唯一的令牌,那么当页面过多时,服务器由于生产令牌而导致的负担也会增加。而使用会话等级的令牌代替的话,服务器的负担将没有那么重
2.检查Referer字段
HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于bank.example.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于bank.example.com之下,这时候服务器就能识别出恶意的访问
这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能
3.添加校验token
由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是窗体中的一个数据项。服务器将其生成并附加在窗体中,其内容是一个伪随机数。当客户端通过窗体提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求
4.设置会话Cookie的SameSite属性为Strict
此时任何跨站请求都不会携带会话Cookie,CSRF攻击也就不存在了