当前位置:首页 > 网络安全

什么是CSRF令牌?

​译者 | 陈峻

审校 | 孙淑娟

跨站点请求伪造(Cross-site request forgery ,又称:跨站点引用伪造)是一种针对Web应用的攻击形式 。黑客通过伪装恶意请求 ,诱骗用户运行他们本不打算执行的任务。尽管CSRF可能听起来与XSS攻击类似,但它们的执行方式存在根本差异 。对此 ,Web服务器需要一种机制 ,来确定浏览器所产生的请求,是否源于合法用户的真实意图,源码库而非受攻击的胁迫 。针对此类问题,服务器端可以生成一个唯一的、且不可预测的密钥值,作为CSRF令牌被包含在客户端的HTTP请求中 。当有后续请求发出时 ,Web服务器会验证包含了令牌的请求参数 ,以拒绝那些不包含有令牌的请求参数 。由于黑客几乎不可能构造一个完整 、有效的云计算HTTP请求来欺骗Web用户,因此该方法通常可被用于防范CSRF攻击 。下面 ,我将和您讨论CSRF令牌的工作原理,及其在应用安全中的重要性。

一 、为什么需要有效的CSRF令牌? 

CSRF令牌通常被建议添加到所有状态更改(state-changing)的请求中   ,以便在后端被执行验证。由于只有应用服务器和客户端可以识别令牌 ,因此后端必须确保传入的请求包含有效的CSRF令牌 ,服务器租用以避免XSS或跨站点请求伪造攻击的得逞 。

在基于Cookie的会话期间,作为密钥值的CSRF令牌需要被安全处理以保持有效。为此,令牌应当被放置在HTML表单的隐藏字段中,被传输到客户端,并使用HTTP的POST请求被提交。作为优秀的实践,我们建议使用标准的标头来验证请求的香港云服务器来源 ,并使用其他措施去识别和比较来源和目标。如果来源匹配 ,则判定请求是合法的;如果不匹配,则表明疑似跨域请求,应予以丢弃 。

二 、CSRF令牌在防止攻击中的意义 

由于令牌在生成过程中使用到了伪随机数(pseudo-random number)生成器、静态密钥 、以及种子时间戳 ,因此CSRF令牌的值是不可预测的免费模板 。同时 ,每个用户的令牌也是不同的 ,而且只会存储活动的用户会话。据此,安全团队可以通过将随机数生成器的输出 ,与用户特定的熵(entropy)连接起来,并对整个结构进行散列处理,以提高令牌值的唯一性 。源码下载对此,黑客将很难在早期的会话Cookie中,根据已发布的令牌样本,去猜测CSRF令牌 。

三 、如何使用CSRF令牌 

尽管我们可以在URL查询字符串中放置令牌,但是查询的字符串记录会被留存在服务器和客户端的多条记录中 。因此 ,查询字符串可以在客户端屏幕的浏览器上被访问到,甚至会在HTTP引用标头中,被传输给第三方应用程序 。可见,这种方法是不安全的 。对此 ,我们建议CSRF令牌应该被存储在服务器端的应用程序中 ,并在自定义请求标头中传输CSRF令牌。服务器端应用程序通过按需验证每个请求,以确保各种有效的请求,包含了与用户活动会话中存储的值相匹配的令牌 。同时 ,CSRF令牌也可以对包括POST、PUT和DELETE在内的所有HTTP方法执行验证。

四、如何在Java中实现CSRF令牌 

由于Java应用缺乏了针对CSRF攻击的固有保护 。因此 ,我们建议在Java中实现CSRF令牌的时候,请使用一个过滤器和一些辅助类 ,来启用令牌的创建 、资源的分析  、以及响应的构建 。例如 ,您可以使用通用无状态(Generic Stateless)过滤器,来实现了双重提交(double-submit)的Cookie模式 ,以启用CSRF保护。同时,您可以采用如下工作流程 :首先 ,如下面代码段所示,过滤器会在Java应用的web.xml文件中被定义 :

复制<filter><filter-name>CSRFFilter</filter-name><filter-class>com.github.adriancitu.csrf.GenericCSRFStatelessFilter</filter-class><filter><filter-mapping><filter-name>CSRFFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>1.2.3.4.5.6.7.8.

该过滤器包括了两个可选的初始化变量 :

1.csrfHeadername– 包含了令牌的标头名称

2.csrfCookieName– 用于存储CSRF令牌的Cookie的ID 。

对于每个HTTP请求 ,过滤器都会提供一个ExecutionContext类的实例。它包含了CSRFcookie、以及HTTP请求与响应的Java对象  。同时 ,该类也包含了ResourceCheckerHook 、ResponseBuilderHook和TokenBuilderHook等辅助类的实现 。

接着 ,过滤器会根据如下三种情况  ,去检查所请求的HTTP资源的保护状态 :

1.MUST_NOT_BE_PROTECTED

2.MUST_BE_PROTECTED_BUT_NO_COOKIE_ATTACHED

3.MUST_BE_PROTECTED_AND_COOKIE_ATTACHED

对于状态为MUST_NOT_BE_PROTECTED和MUST_BE_PROTECTED_BUT_NO_COOKIE_ATTACHED的资源,过滤器会生成一个由TokenBuilderHook类提供的CSRF令牌的Cookie。对于带有标签MUST_BE_PROTECTED_AND_COOKIE_ATTACHED的资源,过滤器则使用ResourceCheckerHook去检查资源的CSRF保护状态,然后使用类ResponseBuilderHook向客户端返回一个响应 。当然 ,上述代码只是一个参考示例,在实际运用中,还需要开发团队在源代码中进一步构建CSRF缓解机制。

五 、如何在PHP中实现CSRF令牌 

由于PHP使得开发人员能够创建具有交互功能的动态网站  ,因此它在内容管理系统领域备受欢迎 。不过 ,我们需要在PHP联系人和用户输入的表单中 ,通过实现post处理程序,对传入请求予以验证,让网站免受CSRF的攻击。在PHP联系人表单中 ,实现CSRF保护的典型工作流程如下 :首先 ,我们需要在登录页面上创建一个表单的footer脚本 。该脚本会调用SecurityService(一个PHP类),生成令牌 ,并启动PHP会话。SecurityService会被写入这个用于验证请求的令牌,并将令牌加载到隐藏字段中。如下代码展示了典型的SecurityService.php配置:

复制public function getCSRFToken()

{

if (empty($this->session[$this->sessionTokenLabel])) {

$this->session[$this->sessionTokenLabel] = bin2hex(openssl_random_pseudo_bytes(32));

}

if ($this->hmac_ip !== false) {

$token = $this->hMacWithIp($this->session[$this->sessionTokenLabel]);

} else {

$token = $this->session[$this->sessionTokenLabel];

}

return $token;

}1.2.3.4.5.6.7.8.9.10.11.12.

左右滑动查看完整代码然后 ,页面上呈现PHP联系人表单,以便用户输入诸如主题 、消息  、姓名和电子邮件等详细信息 。表单还应当在隐藏字段中包含已生成的标记--csrf-token。在用户单击提交按钮后 ,应用程序将执行JQuery表单验证,并将各种参数发布到PHP上。

此外,在联系人表单被提交后 ,该表单会执行一个脚本 ,将已嵌入的令牌与会话中存储的令牌进行比较。如果令牌匹配  ,应用程序会去响应用户的请求。如果不匹配,PHP将通过错误消息去告知用户 。

六 、如何在Django中实现CSRF令牌 

Django提供了一个开箱即用的CSRF中间件标签,您可以轻松启用它 ,以抵御CSRF攻击。如下工作流将向您展示如何在已有的框架内 ,实现针对CSRF的防护 :在Django中,CSRF中间件是被默认启用的。如果开发人员想覆盖此设置  ,则应该在任何视图之前事先声明django.middleware.csrf.CsrfViewMiddleware ,以启用CSRF令牌验证。对于一些特定视图 ,开发人员可以调用csrf-protect装饰器(decorator)。此类装饰器可用于在输出中插入CSRF令牌的视图。如下代码段展示了装饰器的基本配置:

复制from django.shortcuts

import render

from django.views.decorators.csrf

import csrf_protect

@csrf_protect

def my_view(request):c = { }

# ...

return render(request, "a_template.html", c)1.2.3.4.5.6.7.

左右滑动查看完整代码开发人员可以通过在<form>元素中包含csrf_token标签,来启用对使用POST表单的模板的保护 。当然,这仅适用于那些具有内部URL的表单,并不适用于针对外部URL的POST表单。对于外部URL ,我们可以使用如下方法 ,来调用CSRF令牌的标记 :

复制<form method="post">{ % csrf_token %}1.

此外 ,我们还建议使用RequestContext ,在各种相应的Django视图中呈现响应。

七  、如何在Javascript中实现CSRF令牌 

所有Javascript框架都带有实现CSRF防护的默认选项 。例如 ,Express.js就包含了csurf中间件 ,可协助创建和验证各种令牌  。请使用如下流程来启用csurf,并达到CSRF的防护效果 :首先  ,请在index.js文件中包含以下代码:

复制app.get(/, csrfProtection, (req, res) => { res.render(index, { csrfToken: req.csrfToken() });1.2.

左右滑动查看完整代码接着  ,请使用类似如下代码的配置 ,将index.ejs添加到各个视图文件夹中 :

复制<input type=hidden name=_csrf value=<%= csrfToken %>><label for=name> Name:</label><input type=text name=name><button type=submit> Update </button></form>1.2.3.4.5.

左右滑动查看完整代码主配置文件中的“/”路由会在index.ejs模板中呈现csrfToken变量,并在隐藏字段中插入令牌  。当用户提交表单时,对应的请求会被添加到“/profile” 路由中 ,以供CSRF令牌验证。如果缺少此CSRF令牌,应用程序将返回一个无效的CSRF令牌错误。

八、如何修复无效的CSRF令牌 

正如前文所述,CSRF攻击将导致用户会话被未经验证的访问,并产生严重的后果。为了防范此类攻击,确保用户使用有效令牌来发布请求,我们需要通过如下方法来及时修复和防止无效令牌 :

使用自定义的请求标头

在易受攻击的应用程序中 ,添加CSRF令牌往往需要更改用户界面等复杂的管理任务。而作为替代方案 ,安全团队可以构建自定义的请求标头 ,以使用同源策略(same-origin policy)来加强CSRF防御。安全策略通过强制限制自定义的标头只能在Javascript中被构建 ,并且只能在其源头中被使用的方式,来拒绝各种跨域请求。

利用内置和现有的CSRF处置措施

在大多数情况下 ,许多开发框架都包含了,内置于安全套件中的同步器令牌的防御机制 。它们可以有效地保护整个应用程序的技术栈 。如果在开发团队现有的技术栈中,所用到的框架已经内置提供了默认的抵御CSRF的选项 ,那么您完全可以尝试着在此基础上,根据业务用例的实际 ,进行自定义和配置实施 。

部署基于UI的CSRF防御

CAPTCHA、重新验证(re-authentication)的认证机制、以及一次性令牌之类的机制 ,都可以通过分析请求 ,来防范那些通过CSRF进行未经授权的操作 ,并过滤掉各种非法操作请求。它们虽然提供了强大的验证与防御措施,但是我们需要通过部署UI来改变用户的体验,以便更好地处理关键性的安全操作。

原文链接:https://dzone.com/articles/what-is-a-csrf-token

译者介绍

陈峻 (Julian Chen) ,51CTO社区编辑 ,具有十多年的IT项目实施经验,善于对内外部资源与风险实施管控 ,专注传播网络与信息安全知识与经验;持续以博文 、专题和译文等形式 ,分享前沿技术与新知;经常以线上、线下等方式,开展信息安全类培训与授课。​

分享到:

滇ICP备2023000592号-28