为什么需要 CSRF 代币?

信息安全 饼干 网页浏览器 csrf
2021-08-31 02:00:27

似乎整个问题都可以通过简单地在 HTTP cookie 规范中添加一个新标志来非常优雅地解决。

类似于标记的 cookieSecure仅由用户代理通过安全连接提交,而标记的 cookieHttpOnly将永远无法通过 DOM 访问访问,为什么不指定一个新的标志,比如NoCSRSameOriginOnly,当设置时,它会阻止 cookie被跨域引用者触发的请求提交?

当然,根据HTML5 设计原则 #2.1,它默认为关闭,以免破坏现有网站的先前预期行为我想确实存在一个安全漏洞,但不是一个不能轻易解决的漏洞:不能不加选择地依赖它,因为旧的浏览器可能会忽略无法识别的标志。

那么,为什么不创建一个白名单枚举 -NoCSR实现用户代理版本的相应 UA 字符串,然后只接受/处理带有这些User-Agent:值之一的需要授权和敏感的请求呢?对于没有或未列入白名单的 UA 标头的请求,可能会返回错误,合理地要求用户升级到受支持的浏览器版本。

白名单可以用各种服务器端语言的规范实现库(它可能更像一个简单的函数)干掉。

这似乎不是比当前生成、跟踪和读取安全 CSRF 令牌的系统简单得多吗?

想法,有人吗?

4个回答

您是对的,您的解决方案(仅在同一来源上有效的 cookie)将阻止反 CSRF 令牌对 CSRF 攻击是必要的。至于为什么没有人实现这一点,我们只能猜测。可能是因为当前标头值中已经存在解决方案,即通过检查Referer标头。使用引荐来源网址也比NoCSR标志更灵活:您可以将多个域或子域列入白名单。

请注意,您的提议不会神奇地解决所有问题:当有人单击链接并打开他们登录的页面时,也会使用 cookie。假设您搜索“stackoverflow”并单击顶部结果,那么您想成为即使请求来自另一个来源(即您的搜索引擎),也已登录。因此,您需要两个 cookie:一个带有建议的标志,一个没有。或者,或者,该标志不适用于 GET 请求,以便正常链接工作,但是当他们修改服务器状态以响应 GET 请求时,您会留下很多网站仍然容易受到攻击(许多应用程序会在您执行请求时执行操作,例如GET /deleteUser/123)。

就好像浏览器应该只包含一个关于请求是否跨源的标志,然后 Web 服务器可以决定这是否是一个不应该跨源执行的危险操作......这听起来很像Referer标题!

自从提出这个问题以来,现代浏览器中就添加了这样一个 cookie 标志:SameSite. 从规格

5.3.7. SameSite 属性

如果属性名称不区分大小写匹配字符串
“SameSite”,用户代理必须按如下方式处理 cookie-av:

  1. 如果 cookie-av 的属性值不是“Strict”或“Lax”的不区分大小写匹配,则忽略“cookie-av”。

  2. 如果 cookie-av 的属性值是“Lax”的不区分大小写的匹配,则“enforcement”为“Lax”,否则为“Strict”。

  3. 向 cookie-attribute-list 附加一个属性,其属性名称为“SameSite”,属性值为“enforcement”。

5.3.7.1。“严格”与“宽松”执法

“严格”执行模式下的同站点 cookie 不会与从跨站点文档上下文触发的顶级导航一起发送。如第 8.8.2 节所述,这可能与现有会话管理系统兼容,也可能不兼容。为了提供降低 CSRF 攻击风险的插入机制,开发人员可以将“SameSite”属性设置为“Lax”强制模式,以排除发送同站点 cookie 和跨站点请求的异常当且仅当它们是使用“安全”(在 [RFC7231] 意义上)HTTP 方法的顶级导航。

松懈的执行为 CSRF
攻击提供了合理的深度防御,这些攻击依赖于不安全的 HTTP 方法(如“POST”),但没有
提供针对 CSRF 作为一般攻击类别的强大防御:

  1. 攻击者仍然可以弹出新窗口或触发顶级导航以创建“同一站点”请求(如第 2.1 节所述),这只是利用过程中的一个减速带。

  2. 可以利用诸如“<link rel='prerender'>” [prerendering] 之类的功能来创建“同站点”请求,而不会冒被用户检测到的风险。

如果可能,开发人员应该使用 8.8.2 节中描述的会话管理机制来
更彻底地降低 CSRF 的风险

据caniuse.com 称,从 IE 11(仅在 Windows 10 上)、Edge 16、Firefox 60、Chrome 51、Safari 12 和 Opera 39开始支持它

我会说主要是并发症。通过另一个应用程序发送的链接不会携带站点引荐来源网址,因此这是一个需要考虑的漏洞。可以只专注于从站点内部传递请求,但可能存在一个 XSS 漏洞,它会创建类似 CSRF 的行为,可以通过使用 nonce来阻止。

非常简短的总结:nonce 方法已经有效并且实现起来并不复杂。这种提议的方法可能最终实现起来要复杂得多,会破坏向后兼容性,并且需要现有的 nonce 方法作为权宜之计。

最后,还有其他非重复行为,您可能仍希望使用 nonce。

这似乎不是比当前生成、跟踪和读取安全 CSRF 令牌的系统简单得多吗?

不,因为这种措施只能防止状态变化的端点(例如表单数据的 HTTP POST),但 CCRF 的另一种风格只是让用户点击恶意形成的链接(例如check out this facebook <a href="yourbank.com/?s=somethingEvil">post</a>->“查看此 facebook 帖子”)不检查网址。

我知道 GET 请求应该被认为是“安全的”(请参阅https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html),但是如果您要让浏览器实现一个标准来防止 CSRF ,那么它必须保护所有情况。服务器实现不安全的 GET 请求时的事件。

预防 CSRF 需要多个步骤,包括 CSRF 代币,预防的详细说明可以在此OWASP 备忘单中找到总之,它需要:

  • 检查您的框架是否已内置 CSRF 保护并使用它。如果不向所有状态更改请求添加 CSRF 令牌
  • 始终对会话 cookie 使用 SameSite Cookie 属性
  • 至少实现以下一项:使用自定义请求标头,使用标准标头验证来源,使用双重提交 cookie
  • 考虑为高度敏感的操作(OTP、重新验证等)实施基于用户交互的保护
  • 请记住,任何跨站点脚本 (XSS) 都可以用来破坏所有 CSRF 缓解技术
  • 不要使用 GET 请求进行状态更改操作