4.4 抵御CSRF攻击

你可以通过多种方式修补CSRF漏洞。一种最为流行的抵御CSRF攻击的方式就是采用CSRF令牌。当受保护的站点接收到浏览器提交的有可能修改数据(例如POST请求)的请求时,需要验证CSRF令牌。在这种情况下,Web应用(就如Bob的网银)将会产生一个由两部分组成的令牌:一部分由Bob接收,而另一部分由应用保留。当Bob试着发起转账请求时,他将会提交他的那部分令牌,网银将把该部分令牌与它保存的那部分令牌合在一起进行令牌有效性验证。这种基于令牌的设计使得令牌很难被猜测,并且只有被分配了令牌的特定用户(例如,Bob)才有权限访问。另外,这些令牌不总是显式命名的,但有一些潜在的命名例子,例如X-CSRF-TOKEN、lia-token、rt、form-id等。令牌可以存放在HTTP请求头、HTTP POST体或者隐藏域中,如下例所示:

在本例中,网站可以通过cookie、嵌入网站中的一段脚本或者网站中传输的部分内容来获取CSRF令牌。不管网站如何获取令牌,仅有授权的目标浏览器能够知道和读取令牌的内容。因为攻击者不能提交令牌,所以他们不能成功地提交POST请求,也就不能执行CSRF攻击。然而,在寻找可以利用漏洞的过程中,并不会因为网站使用了CSRF令牌,就使整个过程进入一个死胡同。黑客可能会尝试删除令牌、修改令牌的值等以确保服务器认为令牌已经被正确实现。

另外一种抵御CSRF攻击的方式是采用CORS,然而,这并不是万无一失的,因为它依赖于浏览器的安全性,并确保已经进行了正确的CORS配置来限定第三方站点何时可以访问响应信息。攻击者有时可以通过修改内容类型application/json为application/x-www-form-urlencoded,或者因为服务器端存在误配置而使用GET请求而不是POST请求来绕过CORS。这种绕过行为能够发生,是因为当内容类型是application/json时,浏览器会自动发送一个OPTIONS HTTP请求,而如果请求是GET请求或者内容类型是application/x-www-form-urlencoded时,浏览器不会自动发起OPTIONS HTTP请求。

最后,还有两种很少用到的CSRF防御策略。第一种,网站会检查提交的HTTP请求中Origin或者Referer头中是否包含期望的特定值。例如,在有些情况下,Twitter会检查Origin头信息,如果该头信息不存在,就会检查Referer头信息。这能够产生防御效果主要是因为浏览器控制这些头部信息,但攻击者不能远程设置或修改这些信息(显然,我们不考虑黑客利用浏览器或者浏览器插件中的漏洞,从而修改以上两个头部信息的情况)。第二种,浏览器现在正开始实现对一种新的cookie属性samesite的支持。这一属性可以被设置为strict或lax。当设置为strict时,对于不是源自被保护网站的任何HTTP请求,浏览器都不会发送cookie,这包括最简单的HTTP GET请求的情况。例如,如果你登录了亚马逊(Amazon)网站,并且该网站使用了strict samesite cookie,那么当你通过来自另一个网站的链接访问亚马逊网站时,浏览器并不会提交你的cookie。而且,亚马逊也不会认为你已经登录了,除非你再访问另一个亚马逊的Web网页时,cookie才再次被提交。相反,设置samesite属性为lax表示要求浏览器发送cookie时要带上初始的GET请求信息。这种设计原则基于GET请求永远不会修改服务器端的数据。在这种情形下,如果你已经登录了亚马逊,并使用了lax samesite cookie,当你从别的网站重定向到亚马逊网站时,浏览器将会提交你的cookie,并且亚马逊也会认为你已经登录过了。