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。
我正在努力解决我的 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。