将所有域添加到 CORS 的安全隐患(访问控制允许来源:*)
Posted
技术标签:
【中文标题】将所有域添加到 CORS 的安全隐患(访问控制允许来源:*)【英文标题】:Security implications of adding all domains to CORS (Access-Control-Allow-Origin: *) 【发布时间】:2013-10-19 19:39:30 【问题描述】:据说不是将所有域都添加到CORS,而是应该只添加一组域。 然而,添加一组域有时并非易事。例如。如果我想公开一个 API,那么对于每个想要调用该 API 的域,都需要联系我以将该域添加到允许的域列表中。
我想在安全隐患和减少工作量之间做出有意识的权衡决定。
我看到的唯一安全问题是DoS attacks 和CSRF 攻击。 CSRF 攻击已经可以通过 IMG 元素和 FORM 元素实现。 与 CORS 相关的 DoS 攻击可以通过阻止对引用标头的请求来克服。
我是否遗漏了安全隐患?
===编辑===
假设Access-Control-Allow-Credentials
Header没有设置
我知道如何添加给定的域“CORS 访问”列表,因此我只对添加所有域“CORS 访问”的安全影响感兴趣
【问题讨论】:
您已经可以使用 img 标签或 iframe ping url,CORS 只是让 ajax 获取 url。 您的编辑极大地改变了含义。通过不允许任何经过身份验证的请求,这意味着您希望通过 CORS 公开的端点必然仅限于“公共”功能。这样的端点很可能根本不会受到 Access-Control-Allow-Origin: * 的影响,主要是因为你不能对公共端点进行 CSRF 攻击。 我认为Access-Control-Allow-Origin: *
和Access-Control-Allow-Credentials: true
一起使用是不可能的,请参阅developer.mozilla.org/en-US/docs/Web/HTTP/… 和w3.org/TR/cors/#resource-requests
【参考方案1】:
跨站请求伪造攻击无疑是 Access-Control-Allow-Origin 解决的主要问题。
Ryan 关于内容检索当然是正确的。但是,关于提出请求的问题,这里还有更多要说的。许多网站现在提供 RESTful Web 服务,这些服务公开了可能涉及在后端进行重大更改的广泛功能。很多时候,这些 RESTful 服务旨在通过 XHR(例如 AJAX)请求(可能使用“Single Page Application”作为前端)调用。如果用户在访问恶意第三方站点时有一个活动会话授予对这些服务的访问权限,则该站点可能会尝试在幕后调用这些 REST 端点,传递可能危及用户或站点的值。根据 REST 服务的定义方式,有多种方法可以防止这种情况发生。
在单个页面应用程序的 REST Web 服务的特定情况下,您可以规定对后端 REST 端点的所有请求都使用 XHR 发出并拒绝任何非 XHR 请求。您可以通过检查是否存在自定义请求标头(类似于 jQuery 的 X-Requested-With)来决定这一点。只有 XHR 类型的请求可以设置这些标头;来自表单和嵌入式资源的简单 GET 和 POST 请求不能。最后,我们想要指定 XHR 请求的原因让我们回到了最初的问题——XHR 请求受 CORS 规则的约束。
如果您允许Access-Control-Allow-Origin: *
,那么任何站点都可以代表用户向您的 REST 端点发出任何 AJAX 请求。如果您的 REST 端点涉及任何类型的敏感数据或允许数据持久性,那么这是一个不可接受的安全漏洞。相反,像我描述的那样强制执行仅限 XHR 的请求,并定义允许发出这些请求的来源白名单。
值得指出的是,如果您的 REST 端点不公开任何敏感信息,或者如果它们不允许用户进行任何持久性数据更改,那么Access-Control-Allow-Origin: *
可能是合适的决定。例如,谷歌地图提供公共地图数据的只读视图;没有理由限制可能希望调用这些服务的第三方网站。
【讨论】:
您的观点假设 Cookie 与请求一起发送,但默认情况下并非如此。对于要发送的 Cookie,需要将额外的标头Access-Control-Allow-Credentials
设置为 true
如果您使用localStorage
,则不需要任何cookies
我不知道你现在在说什么。如果您需要针对特定用户的任何类型的经过身份验证的会话,则需要某种持久性标识符,该标识符会被传输到服务器。最典型的是,它采用 cookie 的形式,也可以作为 URL 参数或自定义标头来完成。但无论如何,localStorage 根本不会发送到服务器,并且仅对设置它的域可用。所以,我看不出你在这个问题的背景下提出了什么观点。
现在localStorage
通常用于存储会话密钥,该密钥作为 POST/GET 参数随每个请求一起发送。因此不需要 Cookie,因此不需要设置 Access-Control-Allow-Credentials
。正如我对我的问题的编辑指定 Access-Control-Allow-Credentials
实际上未设置。因此不会发送任何 Cookie。
如果您没有设置Access-Control-Allow-Credentials
,并且您进行了无cookie 身份验证(即调用方提供了承载授权标头),那么您不需要将域列入白名单。可以从任何来源调用结构良好的 REST API。【参考方案2】:
除了csauve
的回复外,其他回复都没有回答我原来的问题。
回答我的问题;看来只要不设置Access-Control-Allow-Credentials
就没有安全问题。
(这让我想知道为什么在未设置 Access-Control-Allow-Credentials
时规范需要预检?)
【讨论】:
IIRC 这是一个兼容性的问题:目标是放宽同源策略,但假设服务器可能尚未准备好或无法处理非简单请求(基本上可以通过链接和表单触发的请求),所以这是选择加入的,然后添加了允许协议。【参考方案3】:老问题,但这里有很多不好的答案,所以我必须添加我的。
如果您没有设置Access-Control-Allow-Credentials
,并且您进行了无cookie 身份验证(即调用方提供了承载授权标头),那么您不需要将来源列入白名单。只需在 Access-Control-Allow-Origin
中回显原点即可。
可以从任何来源安全地调用结构良好的 REST API。
【讨论】:
相同的 POV。 IE。似乎只要不设置Access-Control-Allow-Credentials
就没有安全问题。这让我想知道为什么在未设置 Access-Control-Allow-Credentials
时规范需要预检。
我也想知道。为什么我们在不使用 cookie 时需要预检请求?
它仍然是预检,因为授权标头在规范中不被视为“简单”标头。我们不需要它来达到我们的目的,但浏览器无论如何都会这样做。 w3.org/TR/2020/SPSD-cors-20200602/#simple-header 浏览器在实际发出请求之前无法预测是否会设置 Access-Control-Allow-Credentials。【参考方案4】:
您可以发送多个,例如:
Access-Control-Allow-Origin: http://my.domain.com https://my.domain.com http://my.otherdomain.com
但我建议不要这样做。相反,保留允许域的白名单。让我们说:
allowed = [ "X", "Y", "A.Z" ];
如果你收到来自X
的请求,你会回复:
Access-Control-Allow-Origin: X
如果您收到来自 A.Z
的请求,您会回复:
Access-Control-Allow-Origin: A.Z
如果您收到来自不允许的域的请求,请以错误或无 CORS 策略进行响应。
所有 XHR 请求都会发送一个 Origin
标头,所以使用它。您只需发送 OPTIONS
请求的 CORS 策略标头,而不是随后的 GET/POST/HEAD
请求。
我看到的主要问题是您公开了所有域。也许您有一个安全的管理域,例如:https://admin.mydomain.com
,或者您有一个尚未准备好发布的产品网站。您不想包含手头请求并非绝对必要的任何内容。
而*
实在是太懒了。
【讨论】:
您的回答没有解释允许所有域的安全隐患 我认为您已经在问题中涵盖了它。如果您想要权威的回应,我建议您悬赏。 请注意,如果您这样做,您还应该包含Vary: Origin
标头以指示响应标头取决于请求的 Origin
标头。【参考方案5】:
CORS 是关于取回内容,而不仅仅是发出请求。当您通过 img 或 script 标签获取资源时,您可以欺骗某人的浏览器发出 CSRF 样式请求。这是正常的,您可以使用普通的 CSRF 令牌来防止这种情况发生。
在所有域上启用 CORS 后,您现在可以让攻击站点上的 javascript 发出请求并取回内容,从而侵犯他们的隐私。
例子:
想象一下,您的背部为所有域启用了 CORS。现在我创建了一个向 yourimaginarybank.com/balance 发出请求的网站
IMG 请求不会执行任何操作,因为我的 javascript 无法获取您银行网站上该页面的 html 中的内容。现在他们已经打开了 CORS,我网站上的 javascript 实际上会返回一个带有您余额的 HTML 页面,并将其保存到我的服务器。我不仅可以像以前一样发出 GET 请求,而且现在我可以看到里面有什么了。这是一个巨大的安全问题。
如何在不将大列表添加到标题中的情况下解决问题?每个 CORS 请求都使用 Origin 标头发出。最好的解决方案可能是读取 Origin 标头,然后查询数据库以查看它是否按照 Fritz 在他的回答中的建议被列入白名单。
【讨论】:
您的观点假设银行的 Cookie 是随请求一起发送的,默认情况下并非如此。对于要发送的 Cookie,需要将额外的标头Access-Control-Allow-Credentials
设置为 true
【参考方案6】:
最佳实践是首先检查传入请求的域,然后生成响应标头。根据是否允许此域发送请求,将其(仅此一个)添加到 Access-Control-Allow-Origin
响应标头中。
Afaik,甚至不可能向此标头添加多个域。所以它要么是*
,要么是一个特定的域,我总是不想添加*
【讨论】:
您的回答没有解释允许所有域的安全隐患以上是关于将所有域添加到 CORS 的安全隐患(访问控制允许来源:*)的主要内容,如果未能解决你的问题,请参考以下文章