Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-domain communication broken in latest Chrome (115+) #54

Closed
gerneio opened this issue Oct 5, 2023 · 41 comments
Closed

Cross-domain communication broken in latest Chrome (115+) #54

gerneio opened this issue Oct 5, 2023 · 41 comments

Comments

@gerneio
Copy link

gerneio commented Oct 5, 2023

This is mostly a thread to share my experiences with the issues with my cross-domain setup using sysend.js, which stopped working several months ago, but I only now was able to dig in to it and figure out what was happening.

When I tested your built-in cross-domain demo (https://jcubic.pl/sysend-demo/) it still worked, so at first, I thought the issue was due to something with my setup (I only load one iframe since that seemed to be enough for me to get full functionality, see #29 & #37). So I first updated to the latest version of sysend, and when I still had issues, next changed my setup to be configured with dual iframes, but ultimately had the same issues. Lastly, I tried toggling the enforcement of using the localStorage feature via sysend.useLocalStorage(), but no differences.

Switching to evaluating the roots of the problem, broadcastChannel.postMessage would never propagate from an iframe window to a tab with the same domain when that iframe was rendered under a page with a different domain. The same holds true for any localStorage events (all of the localStorage data was completely different and isolated, even though the tab and iframe were from the same origin).

In my case my two sites are different domain origins entirely (a.subdomain1.com vs b.subdomain2.com), where as your cross-domain demo are both under the same domain, just one is a subdomain (jcubic.pl vs terminal.jcubic.pl) which is why I suspect the demo still works, although I couldn't find any documentation anywhere for that exception. I did try the setup for my domains with different subdomains but under same main domain, and it did work, but unfortunately I can't make that change in production since it'll break a lot of users access (on both sites) to various resources (too many hardcoded URL's in emails).

Ultimately, the issue is due to a new feature coming down the pipe in browsers, storage/state propagation. I investigated Chrome specifically since that's what we work with internally, so the situation for Firefox or Edge could be different. For Chrome, this feature started in Chrome Beta 113, but was officially available in the production release of Chrome 115 (I'm on 117 atm), which was released back in July '23 I think, which kind of lines up with when I suspect this issue started to occur for me. FYI, I believe that they are rolling out this feature to users slowly, so not all users on latest Chrome version may be affected by it.

For Chrome, there is a way to register in an Origin Trial and submit a form which essentially will generate you a code that you then embed in your site (as a meta tag, or a http header in some cases). You have to provide it your top-level site domain and specify some other settings (i.e. subdomain wildcard, third-party script injection, usage count, etc). Ultimately, this allowed me to restore this functionality to my site in the latest chrome browser version without needing all users to disable a chrome feature flag or anything like that, HOWEVER, this code/trial will expire (supposedly) by Sept 2024 (link), which if anything, at least gives me a year to figure out an alternative (or actually migrate to using same domain origin). I'm hoping that they enable some sort of way to enable a whitelist of allowed domains to have unpartioned cross-origin access to these resources before the trial expiration date.

If anyone has any other suggestions or knows more about this, I'm all ears. But until then, I hope this helps someone else.

Other useful links:

https://developer.chrome.com/en/docs/web-platform/origin-trials/#take-part-in-an-origin-trial
https://developer.chrome.com/docs/web-platform/origin-trial-troubleshooting/
https://developer.chrome.com/docs/web-platform/third-party-origin-trials/
https://chromeenterprise.google/policies/atomic-groups/#ThirdPartyStoragePartitioningSettings

Credit to @yangon99's comment which was my first hint of the source of the issue being related to the new storage partitioning feature.

@jcubic
Copy link
Owner

jcubic commented Oct 5, 2023

Thanks for bringing this to my attention. I need to read more about Storage Partitioning and decide what to do. Maybe I will just update the docs (README) with a warning about this security feature.

@jcubic
Copy link
Owner

jcubic commented Oct 5, 2023

I've asked the question where you are supposed to give feedback about the API.

@slemus29
Copy link

Any updates about this issue, im having same problem with the latest version of chrome

@jcubic
Copy link
Owner

jcubic commented Nov 17, 2023

You can follow the updates here privacycg/storage-access#102

@jcubic
Copy link
Owner

jcubic commented Nov 17, 2023

I think you should look into issues with the title "Extending Storage Access API (SAA) to non-cookie storage". It looks promising but we need to wait for it to be implemented in browsers.

@gerneio
Copy link
Author

gerneio commented Dec 4, 2023

Looks like something is happening per this new comment.

Would be waiting on Chrome 121 Stable mid Jan '24 release which should have support for the broadcast channel "fix", which is what we need, but we can probably test in beta a bit sooner.

So far the origin trial has been working fine for me, but that'll expire Sept '24.

@jcubic
Copy link
Owner

jcubic commented Dec 4, 2023

Yes, I will test with Beta and see how it will work.

@jcubic
Copy link
Owner

jcubic commented Dec 11, 2023

I was able to make it work with localStorage. BroadcastChannel doesn't work yet even in Beta 121.

In order to use the code you need to register for the Origin Trail: StorageAccessAPIBeyondCookies.

But the library just works:

You can test it at:
https://jcubic.pl/sysend.php?_=local
https://just.net.pl/sysend.php?_=local

@jcubic
Copy link
Owner

jcubic commented Dec 11, 2023

I was too fast, there is still an issue with sending messages.

@jcubic
Copy link
Owner

jcubic commented Dec 11, 2023

It's working now, I've used the wrong token, I though that it was the same token for both domains.

@jcubic
Copy link
Owner

jcubic commented Dec 12, 2023

There were some issues with the library when localStorage was used, the sysend.ist() was not returning proper values on init (in the update track event) and when refreshing the page sometimes the demo didn't have the other window in the select. This is fixed in 1.17.1.

Now everything in the demo should work.

Also, the API may change but they added BroadcastChannel as a function, not a constructor. They said it would be odd to have new in front of the method.

@jcubic
Copy link
Owner

jcubic commented Dec 12, 2023

One more update for the code and the tracking events. Note that this works properly only in Chrome 121 (I'm not sure why the code doesn't work properly - only tracking - in Chrome 120).

Also few updates:

  • in Firefox the library throws an error when calling document.requestStorageAccess without user interaction.
  • In Chrome when the user blocks third-party cookies in settings it will give permission errors

For both cases the user needs to make an interaction with the iframe and requestStorageAccess needs to be triggered in an event that is triggered by the user.

@jcubic
Copy link
Owner

jcubic commented Dec 12, 2023

So I tested on Beta 121 and stable 120. On the Beta version, the tracking works 10/10 times when I refresh the page. But in stable 120 it is like 3/10 when it works. I hope when the 121 will be released it will work as expected.

@mdxiaohu
Copy link

mdxiaohu commented Jan 12, 2024

Perhaps I have encountered a similar problem as well.
This is an error message thrown by my browser.
sysend.js:17 requestStorageAccess: May not be used in an insecure context.
My Google Chrome version number is 120.0.6099.217.
At present, cross domain communication is not effective, and we do not know how to solve it temporarily.

I reread the question.
@gerneio A solution has been proposed, which requires a domain name to be implemented.
for example:
<meta http-equiv="origin-trial" content="token">
Without a domain name, we are temporarily unable to apply for a token.

@jcubic
Copy link
Owner

jcubic commented Jan 12, 2024

@mdxiaohu The error "May not be used in an insecure context" means that you don't use HTTPS.

@mdxiaohu
Copy link

mdxiaohu commented Jan 12, 2024

@jcubic Thank you for your reply.
Yes, my website does not have a domain name and cannot use HTTPS.
I am currently considering a solution to this problem.
I don't know if it's possible to use PostMessage to enable cross domain communication in the current library.

The difficulty of this issue is that our three systems do not have domain names and cannot use HTTPS, as they are deployed on the internal network.
I have no problem using this library in a lower version browser, but cross domain communication fails in a higher version browser. There seems to be no better solution at the moment, and I will continue to monitor the fix of this feature.

@jcubic
Copy link
Owner

jcubic commented Jan 12, 2024

But do you use an IP address instead of a domain name? You don't need to have a project public over the internet to have a domain name.

@mdxiaohu
Copy link

But do you use an IP address instead of a domain name? You don't need to have a project public over the internet to have a domain name.但是你用IP地址而不是域名吗?您无需在互联网上公开项目即可拥有域名。

Yes, only IP can be used. The rest is not up to me to make decisions, after all, it is the customer's request.

@jcubic
Copy link
Owner

jcubic commented Jan 12, 2024

I don't know what is your setup but adding DNS for internal network should not be that hard. It will also make life easier since you will not need to keep remembering the IP addresses to access application.

When I was working for Roche all internal application were roche.com or roche.net subdomains even that the names were not public. But I'm not sure how hard is to set it up.

@jcubic
Copy link
Owner

jcubic commented Jan 12, 2024

I can see if I can disable the requestStorageAccess for no https but if you have different origins with IP addresses you will not be able to use Cross-Origin in Chrome. You need to setup DNS and get the token for Origin Trail.

@mdxiaohu
Copy link

mdxiaohu commented Jan 12, 2024

Different national conditions make it difficult not for technology, but for leadership requirements.

For example:
Project A's address: http://192.168.0.50:10066.
Project B's address: http://192.168.0.50:10067.
Project C's address: http://192.168.0.50:10068.
It's obvious that it's cross domain

”You need to setup DNS and get the token for Origin Trail.“
"There is a new API: Storage Access API. It's available when you register for the Origin Trial."

I tried to obtain a token today, but when I filled in the IP address, it prompted me that the verification did not pass. When I fill in the domain name, I can obtain the token normally.
I'm not sure if obtaining a token requires using a domain name.

My hope:
There are three tabs in the browser, with systems A, B, and C opened separately. Notify systems B and C when data changes occur in system A.
For example, if I manually change the token value of system A, it triggers event notifications B and C.
I spent a lot of time searching for open-source libraries for this, and finally found "sysend. js".
He supports cross domain communication and multi tab communication, which is great and meets my business needs.
However, currently it seems that cross domain communication is not working in higher versions of browsers, mainly due to browser limitations.

@mdxiaohu
Copy link

I can see if I can disable the requestStorageAccess for no https

Okay, thank you for your work and reply. I will continue to monitor the progress.

@jcubic
Copy link
Owner

jcubic commented Jan 12, 2024

I can check maybe port difference don't fall into storage partition. Try using version 1.16.3 maybe you don't need all of this.

@mdxiaohu
Copy link

You are really too strong, thank you very much.
In version 1.16.3, there is no longer an error message for requestStorageAccess.
Even in the latest version of Google Chrome, cross domain communication is still available.
I have reviewed the code for version 1.16.3, which no longer includes the requestStorageAccess method.
I don't know if it's possible to add a judgment in a new version in the future.
As mentioned earlier.

I can see if I can disable the requestStorageAccess for no https

@jcubic
Copy link
Owner

jcubic commented Jan 15, 2024

Ok, so if cross-domain communication works then probably a different port doesn't create different partitions same as with the subdomain.

I can add check window.isSecureContext to the if statement but still, there is an issue with calling the document.requestStorageAccess outside of a user-generated event. I'm not sure how to fix this. The only thing that came into my mind is to make the iframe visible and add a button that the user will click. I asked here if there is a possibility to not require this (since there is a popup that accepts permission) privacycg/storage-access#192 but so far got no reply.

@mdxiaohu
Copy link

Thank you very much for your reply and help.
According to the concept of cross domain, web pages will also cross domains depending on the port. I don't know why it works, but it does work without the requestStorageAccess method.
I think I will wait for the new version to be released so that it can include disabling requestStorageAccess in certain special circumstances.
At that time, I will use a new version to test this feature.

Snipaste_2024-01-15_19-25-14

@jcubic
Copy link
Owner

jcubic commented Jan 15, 2024

There are two concepts here Cross-Originn request and Storage Partition that were added in recent Chrome. Cross-origin is sending requests to a different origin (different port qualify) but storage partitioning is a Chrome feature that blocks 3rd party cookies and other storage. You can access data from a different partition unless you request permission.

The partition is not triggered in Chrome for subdomains on the same domain. And it seems the same happens with different ports. Because Chrome thinks that this is secured and considers the same domain according to this new Feature.

@mdxiaohu
Copy link

At least based on current performance, the functionality is available when the ports are different.
In version 1.16.3, I saw the use of PostMessage for cross domain communication, which was very effective.

I can see if I can disable the requestStorageAccess for no https

Regarding the init method, this is its implementation.
If future versions can be compatible with PostMessage instead of using requestStorageAccess as a fixed feature, it will be highly anticipated.
Snipaste_2024-01-16_16-30-44

@jcubic
Copy link
Owner

jcubic commented Jan 16, 2024

requestStorageAccess is not used instead of PostMessage. It's only additional code that is required for Cross-Domain communication in recent Chrome.

I think that I will just expose the API somehow and make the user call this function inside an iframe where they want to access a different domain. This will also make sure that it will be user responsibility to call it in user triggered event.

@jribbens
Copy link

jribbens commented Feb 6, 2024

I am having the problem that if I do:

remote = window.open('https://example.com/')
setTimeout(() => remote.postMessage('foo', '*'), 5000)

Chrome silently refuses to send the message unless the source window and new window have the same origin. Firefox and Safari however send the message fine regardless of cross-origin status. I am having trouble understanding this behaviour, because the whole point of postMessage is to enable cross-origin communication! Does anyone know what's going on or how to fix it?

(Apologies if this is the wrong place to ask, but I can't find anyone discussing this fundamental brokenness anywhere, and while I have no idea what anyone is talking about in the conversation above, it sounds related.)

@jcubic
Copy link
Owner

jcubic commented Feb 6, 2024

You probably have other errors in your code that you don't show. Since your code works just fine:

See: https://jcubic.pl/post.html

Tested in Chrome 121 and Firefox 122 on Fedora. If your code doesn't work if you create a simple reproduction I suggest reporting an issue to the Chromium project.

@jribbens
Copy link

jribbens commented Feb 6, 2024

You probably have other errors in your code that you don't show. Since your code works just fine:

See: https://jcubic.pl/post.html

Tested in Chrome 121 and Firefox 122 on Fedora. If your code doesn't work if you create a simple reproduction I suggest reporting an issue to the Chromium project.

Thanks. I investigated further in response to your message and it turns out that it's due to the sending site (not the receiving one) setting the HTTP header Cross-Origin-Opener-Policy: same-origin. This is not what the MDN page on that header describes it as doing so I don't understand why that happens, but at least I know now why I'm seeing inconsistent behaviour.

(That header being set on the receiving site also blocks postMessage, which makes marginally more sense but still makes postMessage completely useless, so I guess I have to resort to sending messages in the URL fragment...)

@jcubic
Copy link
Owner

jcubic commented Feb 6, 2024

Cross-Origin-Opener-Policy is for window.opener property that can be used on receiver to interact with the page that triggers open. This was a security issue in browsers. It's a way to protect from malicious behavior.

This has nothing to do with postMessage. Can you share your code, it's hard to figure what what is the problem if you don't show your real code.

@jribbens
Copy link

jribbens commented Feb 6, 2024

What I posted literally is my real code. I also tried cut'n'pasting your exact code onto my own site, and as you say it works fine, but if I make either the site hosting post.html or the site hosting recv.html add a Cross-Origin-Opener-Policy: same-origin header then the message does not get received.

@jribbens
Copy link

jribbens commented Feb 6, 2024

You can see it here: https://unequivocal.eu/post.html . It's identical to your code except I made it so it's triggered by a button click rather than on page load, so as to get around pop-up blocking. The recv.html page does not get the message. And at https://unequivocal.eu/post-no-coop.html you can see the exact same page code but the server isn't sending the COOP header, and recv.html receives the message fine.

@jcubic
Copy link
Owner

jcubic commented Feb 6, 2024

And it only happens in Chrome? Why do you need to use that header? can you just get rid of it?

If this only happens in Chrome you can report a bug to Chromium as I said earlier.

@jribbens
Copy link

jribbens commented Feb 7, 2024

Now I understand what's happening I was able to check properly, and Firefox and Safari have the same behaviour.

I can remove the header from the sending server, but the receiving servers are not under my control, which means that postMessage fails effectively randomly and is, sadly, useless, and the only reliable way to pass information to another domain is to put a magic string in the location hash fragment.

The fact that all browsers are doing this is somewhat mysterious given I cannot see anything in the WHATWG HTML standard that specifies it (neither under the postMessage spec, nor the window.open spec, nor under the cross-origin opener policy spec).

@jcubic
Copy link
Owner

jcubic commented Feb 7, 2024

You can try using proxy iframe if you can upload files to the target server. This is what this library is doing. Also note, that you don't need to open the other website with open(). But in recent Chrome there are issues because of Storage Partitioning which this issue is all about.

@jribbens
Copy link

jribbens commented Feb 8, 2024

The only access I have to the target server is that I can ask the site owner to add a single <script> tag to their site template which loads a script from my server. So it ought to be a piece of cake to register a message handler, job done. But if the target server is sending a COOP header then the chances of me getting the site owner to understand what that is and what the consequences are of removing it, let alone getting them to succeed in removing it, are minimal.

jcubic added a commit that referenced this issue Apr 20, 2024
@jcubic
Copy link
Owner

jcubic commented Apr 20, 2024

I just added detection of Storage Access API by checking 'hasUnpartitionedCookieAccess' in document that is new in Chrome 123. As part of the Spec.

@jcubic
Copy link
Owner

jcubic commented Apr 23, 2024

I'm closing this one, it should work now.

@jcubic jcubic closed this as completed Apr 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants