CSRF 保护——JWT 和 CORS 白名单组合是否足够?

CSRF protection - is a JWT and CORS whitelist combination sufficient?

我正在努力解决我的 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 header 请求中查找该值作为 Bearer...受保护的 Phoenix 控制器包括:

plug Guardian.Plug.EnsureAuthenticated

我在 localhost:5000 上设置了一个测试攻击者 运行 来尝试模拟 CSRF 攻击。首先,我尝试了 AJAX 攻击——我从 logged-in window 的 localStorage 复制了一个有效的 JWT 值,并将其设置在我的请求 header 中模拟攻击者。正如预期的那样,这失败了,因为 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>

但这被 Guardian.Plug.EnsureAuthenticated 捕获,设置在 API 的控制器中,因为没有 Authorization header 也没有有效的 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

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

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

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

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

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