基于 REST 的 API 的 CORS 和 CSRF 预防

信息安全 验证 Web应用程序 攻击预防 csrf 休息
2021-09-11 14:44:34

我目前正在努力解决如何防止 CSRF 的问题。

我的第一个解决方案是使用随处建议的令牌。这当然可以解决这个问题:

<img src="http://api.example.com/me/delete">

但我无法理解的是。

GET http://api.example.com/me/delete

确实会失败,因为该请求没有附带有效的令牌,但是是什么阻止了攻击者执行上述操作,而只是执行以下操作

GET http://api.example.com/me/token/generate
// parse the response
GET http://api.example.com/me/delete?token=...

有效令牌已随请求传递,我的用户现在已被删除...


我的 REST API 允许任何请求通过。

header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);

我“需要”的原因是,我不仅通过表单和 JavaScript 在域本身上使用 REST API。但是我在 C++ 中使用 cURL 来做请求。

如果在这种情况下令牌不是“安全”的解决方案,那么更安全/更好的解决方案会/可能是什么?

更进一步,像 Spotify 这样的东西在他们都有自己的“Web API”和音乐软件本身的同时还能做什么?


另外,我知道这有点牵强,但是是否可以允许任何东西访问我的 API,但具有每个来源的身份验证?

因此,如果http://example.com已“登录”API,并不意味着http://some-other-website.com已登录(或在使用 cURL 请求时更进一步)。

我知道这很牵强,因为“解决方案”是检查原始标头,但这可能会被欺骗。

3个回答

同源策略通常会阻止您的情况发生:

GET http://api.example.com/me/token/generate
// parse the response

无法通过向您的域发出请求的第三方域解析响应。由于 SOP,浏览器通常会阻止它。

我这么说是因为您还添加了以下响应标头以放宽同源规则:

header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);

在以下误解下:

我“需要”的原因是,我不仅通过表单和 JavaScript 在域本身上使用 REST API。但是我在 C++ 中使用 cURL 来做请求。

此标头与 cURL 没有任何关系。标头仅由浏览器解析,实现 SOP。它不会阻止对 Web 客户端的直接请求。

解决方案

在您的api.example.com域上,只允许前端使用用户的浏览器通过它发出请求:

Access-Control-Allow-Origin: example.com

在您的example.com站点上,将以下标头与所有 API AJAX 请求一起发送:

X-Requested-With: XMLHttpRequest

有关此信息,请参阅此答案简而言之,如果没有启用 CORS,则无法跨域发送此标头。

在您的 API 后端验证收到请求时是否存在此标头。如果不是,那就是 CSRF 攻击。

在使用 cURL 的服务器端请求中,只需手动添加标头即可。例如

X-Requested-With: cURL

当您验证它是否存在时,这些请求将成功。

最后,使用 cookie 添加一些身份验证。您登录的网站用户应该有一个可以在 cookie 中发送的身份验证票,并且您应该有一个供您的后端cURL请求使用。这将确保没有人可以在未经许可的情况下使用您的 API。您可能希望限制 Web 用户可以使用授权级别执行的操作,否则他们可能会尝试恶意执行只有您的 cURL 服务器工具才能执行的功能。

我希望我理解你的问题。

至于第一部分,通常情况下,由于攻击者的来源与您的服务器不同,因此浏览器不会允许它读取响应,从而阻止它“解析响应”并在后续请求中发送令牌。但是您已明确允许所有使这种保护无效的来源。

此外,生成的令牌显然应该需要某种身份验证,其中用户 A 的令牌对用户 B 无效,因此即使他手动发送令牌,它也是无效的。

第二部分,如果您有任何类型的正确实施的令牌机制,无论请求来自哪里,令牌都会阻止恶意活动。

至于第三部分,如果我正确理解了您的问题:就从浏览器工作而言,CORS 不允许使用 javascript 或任何客户端代码设置原点因此,在 CSRF 攻击需要某个 Origin 值的情况下,攻击者无法使用 Javascript 来欺骗该值。

希望有帮助。

从原始问题来看,我认为 OAuth/JWT 令牌是推荐的方法。对于这些场景,您希望允许其他来源访问您的 API,但您只允许那些经过授权的请求。这是 JWT 令牌提供此级别安全性的地方,因为声明被添加到令牌中,您可以根据这些声明添加授权过滤器。

对于那些希望只允许同源请求、没有用户身份验证并阻止 CSRF 的人来说,带有验证过滤器的防伪令牌可以做到这一点。一个典型的案例可能是联系表格。有关这方面的更多信息,请使用 angularjs 应用程序查看此示例:

http://www.ozkary.com/2016/03/web-api-anti-forgery-token-angularjs.html

希望能帮助到你。