HTML5数据库和localStorage可以跨子域共享吗?

发布于 2024-10-02 13:10:45 字数 193 浏览 7 评论 0 原文

我正在尝试使用 Safari 跨子域共享数据。我想使用 HTML5 数据库(特别是 localStorage 因为我的数据只是键值对)。

但是,似乎无法从 sub.example.com 访问存储到 example.com 的数据(反之亦然)。在这种情况下有什么办法可以共享单个数据库吗?

I am attempting to share data across subdomains using Safari. I would like to use an HTML5 database (specifically localStorage as my data is nothing but key-value pairs).

However, it seems as though data stored to example.com can not be accessed from sub.example.com (or vice versa). Is there any way to share a single database in this situation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心凉怎暖 2024-10-09 13:10:45

2016 年更新

Zendesk 的这个 为我工作。

示例:

Hub

// Config s.t. subdomains can get, but only the root domain can set and del
CrossStorageHub.init([
  {origin: /\.example.com$/,            allow: ['get']},
  {origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);

请注意用于匹配字符串末尾的 $。上例中的正则表达式将匹配 valid.example.com 等来源,但不匹配 invalid.example.com.malicious.com

客户端

var storage = new CrossStorageClient('https://store.example.com/hub.html');

storage.onConnect().then(function() {
  return storage.set('newKey', 'foobar');
}).then(function() {
  return storage.get('existingKey', 'newKey');
}).then(function(res) {
  console.log(res.length); // 2
}).catch(function(err) {
  // Handle error
});

检查https://stackoverflow.com/a/39788742/5064633

Update 2016

This library from Zendesk worked for me.

Sample:

Hub

// Config s.t. subdomains can get, but only the root domain can set and del
CrossStorageHub.init([
  {origin: /\.example.com$/,            allow: ['get']},
  {origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);

Note the $ for matching the end of the string. The regular expression in the above example will match origins such as valid.example.com, but not invalid.example.com.malicious.com.

Client

var storage = new CrossStorageClient('https://store.example.com/hub.html');

storage.onConnect().then(function() {
  return storage.set('newKey', 'foobar');
}).then(function() {
  return storage.get('existingKey', 'newKey');
}).then(function(res) {
  console.log(res.length); // 2
}).catch(function(err) {
  // Handle error
});

Check https://stackoverflow.com/a/39788742/5064633

牵你的手,一向走下去 2024-10-09 13:10:45

有一种简单的方法可以使用跨域任何东西,只需创建简单的页面,该页面将作为托管在您尝试访问的域上的代理 iframe 包含在内,将 PostMessage 发送到该 iframe 并在 iframe 中,您可以进行 LocalStorage 数据库操作。以下是使用 lcoalStorage 执行此操作的文章的链接 。这是向子域中的不同页面发送消息的演示检查源代码,它使用 iframe 和 PostMessage 。

编辑:新sysend.js 库版本(上面使用demo) 如果浏览器支持则使用 BroadcastChannel,但仍然需要 Iframe。最近的版本还简化了跨源消息的使用,您可以在存储库中使用 iframe 的 html(或者您可以使用带有 lib 的单个脚本标记的简单 html 文件),并且在父级中您只需要调用一个函数sysend.proxy('https://example.com'); 其中 example.com 需要有 proxy.html 文件(您也可以使用自己的文件名和不同的文件名)小路)。

There is simple way to use cross-domain anything, just create simple page that will be included as proxy iframe hosted on domain you try to access, send PostMessage to that iframe and inside iframe you do your LocalStorage database manipulation. Here is a link to article that do this with lcoalStorage. And here is demo that send message to different page in subdomain check the source code, it use iframe and PostMessage.

EDIT: New version of sysend.js library (used by above demo) use BroadcastChannel if browser support it, but still it require Iframe. Recent version also simplify using of Cross-Origin messages, you have html of the iframe in repo, that you can use (or you can use simple html file with single script tag with the lib) and in parent you just need to call one function sysend.proxy('https://example.com'); where example.com need to have proxy.html file (you can also use your own filename and different path).

自此以后,行同陌路 2024-10-09 13:10:45

默认情况下,Google Chrome 会阻止来自另一个域中的 iFrame 的 localStoage 访问,除非启用了第 3 方 cookie,iPhone 上的 Safari 也是如此...唯一的解决方案似乎是在不同的域上打开父域,然后发送到子域通过 window.postMessage 但在手机上看起来又丑又狡猾......

Google Chrome blocks localStoage access from an iFrame in another domain by default,unless 3rd party cookie is enabled and so does Safari on iPhone...the only solution seems to be opening the parent domain on a different domain and then sending to to the Child via window.postMessage but looks ugly and shifty on phones...

梦在深巷 2024-10-09 13:10:45

是的。方法如下:

用于在给定超级域的子域之间共享(例如 foo.example.com vs bar.example.com vs example.com ),在这种情况下您可以使用一种技术。它可以应用于 localStorageIndexedDBSharedWorkerBroadcastChannel 等,所有这些都提供了共享功能同源页面,但由于某种原因,不尊重对 document.domain 的任何修改,这会让它们直接使用超级域作为其来源。

注意:此技术依赖于设置 document.domain 以允许不同子域上的 iframe 之间直接通信。该功能现已被弃用。 (截至 2021 年 4 月,它继续在所有主要浏览器中运行。从 Chrome v109 开始,该功能将被禁用除非 Origin-Agent-Cluster: ?0 标头也被禁用已发送。)

注意:请注意,此技术会删除阻止子域上的恶意脚本影响主域窗口的同源防御,反之亦然,可能会扩大 XSS 攻击的攻击面。共享托管还存在其他安全隐患 - 请参阅 有关详细信息,请参阅 MDN document.domain 页面

(1) 选择一个数据所属的“主”域:即 https://foo.example.comhttps://bar.example.comhttps://example.com 将保存您的 localStorage 数据。假设您选择 https://example.com

(2) 对所选域的页面正常使用 localStorage。

(3) 在所有其他 https://*.example.com 页面(其他域)上,使用 JavaScript 设置 document.domain = "example. com";(始终是超级域)。然后还创建一个隐藏的

(4) 隐藏 iframe 页面上的脚本只需 (a) 设置 document.domain = "example.com";,以及 (b) 完成此操作后通知父窗口。之后,父窗口就可以不受限制地访问iframe窗口及其所有对象了!因此,最小的 iframe 页面类似于:

<!doctype html>
<html>
<head>
  <script>
    document.domain = "example.com";
    window.parent.iframeReady();  // function defined & called on parent window
  </script>
</head>
<body></body>
</html>

如果编写用户脚本,您可能不希望向 unsafeWindow 添加外部可访问的函数,例如 iframeReady(),因此改为通知主窗口用户脚本的更好方法可能是使用自定义事件:

    window.parent.dispatchEvent(new CustomEvent("iframeReady"));

您可以通过向主页窗口添加自定义“iframeReady”事件的侦听器来检测该事件。

(注意:即使 iframe 的域已经是 example.com,您也需要设置 document.domain = example.com:为 document.domain 赋值会隐式设置源的port 为 null,并且两个端口必须匹配 iframe 及其父级才能被视为同源。请参阅此处的注释:https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)

(5) 一旦隐藏的 iframe 通知其父窗口它已准备好,父窗口中的脚本就可以使用 iframe.contentWindow.localStorageiframe.contentWindow.indexedDBiframe.contentWindow.BroadcastChanneliframe.contentWindow.SharedWorker 而不是 window.localStoragewindow.indexedDB等等...所有这些对象的范围都将限定在所选的 https://example.com 原点 - 因此它们将为您的所有页面拥有相同的共享原点!

该技术最尴尬的部分是您必须等待 iframe 加载才能继续。因此,例如,您不能在 DOMContentLoaded 处理程序中轻松地开始使用 localStorage。此外,您可能需要添加一些错误处理来检测隐藏的 iframe 是否无法正确加载。

显然,您还应该确保隐藏的 iframe 在页面的生命周期内不会被删除或导航...OTOH 我不知道这样做的结果是什么,但很可能会发生不好的事情。

并且,需要注意的是:可以使用 Feature-Policy 标头来阻止设置/更改 document.domain,在这种情况下,该技术将不会被使用。可以按照描述使用。


然而,这种技术有一个明显更复杂的概括,它不能被功能策略阻止,并且还允许完全不相关的域共享数据、通信和共享工人(即不仅仅是公共超级域的子域)。 @jcubic 已经在他们的答案中描述了它,即:

总体思路是,如上所述,您创建一个隐藏的 iframe 来提供正确的访问来源;但不是直接获取 iframe 窗口的属性,而是使用 iframe 内部的脚本来完成所有工作,并且仅使用 postMessage() 在 iframe 和主窗口之间进行通信>addEventListener("消息",...).

这是可行的,因为即使在不同来源的 Windows 之间也可以使用 postMessage()。但它也明显更加复杂,因为您必须通过在 iframe 和主窗口之间创建的某种消息传递基础设施来传递所有内容,而不是直接在主窗口代码中使用 localStorage、IndexedDB 等 API。

Yes. This is how:

For sharing between subdomains of a given superdomain (e.g. foo.example.com vs bar.example.com vs example.com), there's a technique you can use in that situation. It can be applied to localStorage, IndexedDB, SharedWorker, BroadcastChannel, etc, all of which offer shared functionality between same-origin pages, but for some reason don't respect any modification to document.domain that would let them use the superdomain as their origin directly.

NOTE: This technique depends on setting document.domain to allow direct communication between iframes on different subdomains. That functionality has now been deprecated. (As of April 2021 it continues to work in all major browsers however. From Chrome v109 the feature will be disabled unless an Origin-Agent-Cluster: ?0 header is also sent.)

NOTE: Be aware that this technique removes the same-origin defences that block malicious script on a subdomain from affecting the main-domain window, or visa versa, potentially broadening the attack surface for XSS attacks. There are other security implications for shared hosting as well - see the MDN document.domain page for details.

(1) Pick one "main" domain to for the data to belong to: i.e. either https://foo.example.com or https://bar.example.com or https://example.com will hold your localStorage data. Let's say you pick https://example.com.

(2) Use localStorage normally for that chosen domain's pages.

(3) On all other https://*.example.com pages (the other domains), use JavaScript to set document.domain = "example.com"; (always the superdomain). Then also create a hidden <iframe>, and navigate it to some page on the chosen https://example.com domain (It doesn't matter what page, as long as you can insert a very little snippet of JavaScript on there. If you're creating the site, just make an empty page specifically for this purpose. If you're writing an extension or a Greasemonkey-style userscript and so don't have any control over pages on the example.com server, just pick the most lightweight page you can find and insert your script into it. Some kind of "not found" page would probably be fine).

(4) The script on the hidden iframe page need only (a) set document.domain = "example.com";, and (b) notify the parent window when this is done. After that, the parent window can access the iframe window and all its objects without restriction! So the minimal iframe page is something like:

<!doctype html>
<html>
<head>
  <script>
    document.domain = "example.com";
    window.parent.iframeReady();  // function defined & called on parent window
  </script>
</head>
<body></body>
</html>

If writing a userscript, you might not want to add externally-accessible functions such as iframeReady() to your unsafeWindow, so instead a better way to notify the main window userscript might be to use a custom event:

    window.parent.dispatchEvent(new CustomEvent("iframeReady"));

Which you'd detect by adding a listener for the custom "iframeReady" event to your main page's window.

(NOTE: You need to set document.domain = example.com even if the iframe's domain is already example.com: Assigning a value to document.domain implicitly sets the origin's port to null, and both ports must match for the iframe and its parent to be considered same-origin. See the note here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)

(5) Once the hidden iframe has informed its parent window that it's ready, script in the parent window can just use iframe.contentWindow.localStorage, iframe.contentWindow.indexedDB, iframe.contentWindow.BroadcastChannel, iframe.contentWindow.SharedWorker instead of window.localStorage, window.indexedDB, etc. ...and all these objects will be scoped to the chosen https://example.com origin - so they'll have the this same shared origin for all of your pages!

The most awkward part of this technique is that you have to wait for the iframe to load before proceeding. So you can't just blithely start using localStorage in your DOMContentLoaded handler, for example. Also you might want to add some error handling to detect if the hidden iframe fails to load correctly.

Obviously, you should also make sure the hidden iframe is not removed or navigated during the lifetime of your page... OTOH I don't know what the result of that would be, but very likely bad things would happen.

And, a caveat: setting/changing document.domain can be blocked using the Feature-Policy header, in which case this technique will not be usable as described.


However, there is a significantly more-complicated generalization of this technique, that can't be blocked by Feature-Policy, and that also allows entirely unrelated domains to share data, communications, and shared workers (i.e. not just subdomains off a common superdomain). @jcubic already described it in their answer, namely:

The general idea is that, just as above, you create a hidden iframe to provide the correct origin for access; but instead of then just grabbing the iframe window's properties directly, you use script inside the iframe to do all of the work, and you communicate between the iframe and your main window only using postMessage() and addEventListener("message",...).

This works because postMessage() can be used even between different-origin Windows. But it's also significantly more complicated because you have to pass everything through some kind of messaging infrastructure that you create between the iframe and the main window, rather than just using the localStorage, IndexedDB, etc. APIs directly in your main window's code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文