CSRF 保护 - JWT 和 CORS 白名单组合是不是足够?

Posted

技术标签:

【中文标题】CSRF 保护 - JWT 和 CORS 白名单组合是不是足够?【英文标题】:CSRF protection - is a JWT and CORS whitelist combination sufficient?CSRF 保护 - JWT 和 CORS 白名单组合是否足够? 【发布时间】:2018-07-11 07:25:49 【问题描述】:

我正在努力解决我的 React/Phoenix 应用程序中的 CSRF 漏洞,在我看来我的应用程序是安全的......但我不是这些问题的专家,我想求助于社区看看我是否忽略了某些事情或者我太天真了。

Phoenix 是一个纯 API,与 React 客户端分开运行,所以我正在处理 CORS - 允许来源的白名单在 Phoenix router.ex 中设置:

pipeline :api do
  plug CORSPlug, [origin: "localhost:3000"]
  plug :accepts, ["json"]
  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.LoadResource
end

如您所见,我正在使用 Guardian(它使用 JWT 进行用户身份验证)来处理授权。

授权客户端将 JWT 存储在 localStorage 中,而 Guardian 设置为在请求的 Authorization 标头中查找该值作为 Bearer... 受保护的 Phoenix 控制器包括:

plug Guardian.Plug.EnsureAuthenticated

我设置了一个测试攻击者,在 localhost:5000 上运行以尝试模拟 CSRF 攻击。首先,我尝试了 AJAX 攻击——我从登录窗口的localStorage 复制了一个有效的 JWT 值,并将其设置在我的模拟攻击者的请求标头中。正如预期的那样,这失败了,因为localhost:5000 没有被列入白名单

The 'Access-Control-Allow-Origin' header contains the invalid value 'null'. Origin 'http://localhost:5000' is therefore not allowed access.

为了测试,我将localhost:5000 添加到 Phoenix 白名单中,并且请求确实有效......所以看起来,即使攻击者设法窃取了有效的 JWT,他们也会被白名单阻止。

然后我测试了一个从 OWASP 文档借来的自动表单提交:

<body onload='document.CSRF.submit()'>
    <form action='http://localhost:4000/api/v1/user' method='POST' name='CSRF'>
        <input type='hidden' name='name' value='Hacked'>
        <input type='hidden' name='password' value='Hacked'>
    </form>
<body>

但这会被 API 控制器中设置的Guardian.Plug.EnsureAuthenticated 捕获,因为没有Authorization 标头,也没有有效的 JWT:

[info] POST /api/v1/user
[debug] Processing with MyApp.UserController.create/2
  Parameters: %"name" => "Hacked", "password" => "[FILTERED]"
  Pipelines: [:api]
[info] Sent 401 in 21ms
[debug] MyApp.UserController halted in 
Guardian.Plug.EnsureAuthenticated.call/2

所以我的印象是,即使使用有效的 JWT,AJAX 攻击也会失败,因为 CORS 白名单......简单的请求会失败,因为它们不包含 Authorization 标头。

在使用 JWT 进行授权时,我已经阅读了很多关于 CSRF 保护的内容,但似乎没有两个人可以就什么是安全的和不安全的达成一致。我是否遗漏了什么,或者 CORS 白名单和 Guardian JWT 检查的组合足以保护 CSRF?

【问题讨论】:

【参考方案1】:

您提到了 2 种防止 CSRF 的方法:1)CORS 和 2)存储在 localStorage 中并与用户会话相关联的 JWT。我想同时解决这两个问题:

1) CORS 通过防止来自非来源来源的尝试代表用户发出 HTTP 请求,确实有助于防止某些类型的 CSRF 攻击。这可以防止来自外部来源的 GET/POST,干得好。它不会阻止来自内部来源的 CSRF 攻击。 继续#2...

2) 通过 Authentication/Bearer 标头存储在 localStorage 中的 JWT 可以提供帮助,但仍然容易受到 XSS 攻击。 XSS 预防至关重要。现在 JWT 可以通过 javascript 访问,并且可以通过任何请求传递。为了提供帮助,网站应阻止 javascript 访问令牌。推荐的方法是将 JWT 存储在 HTTPOnly cookie 中。此 cookie 是 Authentication/Bearer 标头的补充。在 Phoenix 服务器端,API 需要做的第一个授权步骤是确保来自 HTTPOnly cookie 的 JWT 与 Authentication/Bearer 标头相同。只有这样才能成功调用API。

【讨论】:

啊,太好了,非常感谢您的解释。我看过你的第二点的各种典故,但这很清楚 - 我以前不明白。 我有一个关于数字 2 的问题。将 JWT 额外保存到 HTTPOnly cookie 是否真的增强了安全性?想象一下,攻击者能够向调用恶意请求 (XSS) 的网站注入一段 JavaScript。当受害者打开站点时,请求被执行 - cookie 也与请求一起发送,因为目标域与 cookie 匹配。我在这里错过了什么吗?保护表单等没有人可以将恶意 JS 放在您的 Web 应用程序上不是更重要吗? @Jonas:问得好,你说得对,#2 不能防止 XSS 攻击。注入 JS 仍然可以代表用户调用恶意请求。请参阅 OWASP 的 note about XSS

以上是关于CSRF 保护 - JWT 和 CORS 白名单组合是不是足够?的主要内容,如果未能解决你的问题,请参考以下文章

Express CORS域白名单

删除 API post 调用上的 csrf 保护

Django CORS 标头白名单不起作用

Firebase CORS,将 IP 范围列入白名单

使用 Apache 将 CORS 列入白名单

如何在 JWT 过滤器中将 api 列入白名单