apache / cordova-ios

Apache Cordova iOS
https://cordova.apache.org/
Apache License 2.0
2.15k stars 987 forks source link

Cookies are blocked in iframe even after disabling ITP #1228

Open abhinavms opened 2 years ago

abhinavms commented 2 years ago

Bug Report

Problem

Even after allowing cross-site tracking in the app setting to disable ITP, the 3rd party cookies are still not being set in iframe. The Storage Access API denies the request to access storage. This causes login to fail in the iframe.

What is expected to happen?

Cookies should be stored and sent in every request

What does actually happen?

Cookies are not being stored

Information

I have created a sample application to illustrate the problem. Link - https://github.com/abhinavms/cordova-3rdparty-cookie

The webview loads URL abhinavms.github.io/cookieTest. It has an iframe that loads cookie-set-test.herokuapp (Source code). cookie-set-test.herokuapp sends a request to set a cookie and verify if the cookie was set. It also displays if the Storage Access API has granted access.

With the latest Xcode version, it is observed that the cookie is never being set in the iframe unless already a first-party cookie was available. This issue was also reproducible in iOS 12.4, which doesn't have ITP

Observations

  1. The iframe does not have the Storage Access API, therefore cookies are not being stored
  2. document.requestStorageAccess() is denying the request to access storage to save cookies
  3. If the 3rd party domain already has a 1st party cookie, then storage access is given and it can load in the iframe

Environment, Platform, Device

Xcode 13.3 iOS 15.3, 12.4

Checklist

abhinavms commented 2 years ago

In mobile and desktop Safari, disabling the "Prevent Cross-Site Tracking" option causes the site to work as expected. In chrome also no issue seems to be there. The only issue is on the mobile app with NSCrossWebsiteTrackingUsageDescription option.

breautek commented 2 years ago

Iframes don't have access to the storage api by default, which is required to handle ITP.

Iframes that is a direct child of the top level frame have an attribute to enable the storage access api. Direct child iframes can have

sandbox="allow-storage-access-by-user-activation" to enable storage access.

From what I read, iframes in which is nested do not have storage access, even with the sandbox attribute.

Note that if either the top frame or the iframe document navigates, storage access is revoked. Any storage grants only persists for the lifetime of the document.

Disclaimer: don't have the means to confirm or test any of this. Just summarizing webkits blog

Also there are some cookie bugs that is unrelated to ITP in WKWebView, which was reported to Apple via https://bugs.webkit.org/show_bug.cgi?id=213510 which I think effects ios <= 13. Your reproduction on ios 12.4, while show similar symptoms is likely related to cookie syncing issues on wkwebview. I believe these are fixed in later ios versions (but I don't work cookies so not sure on the exact details here on this.)

abhinavms commented 2 years ago

breautek If my understanding is correct when I enable "Allow cross-site tracking" in the settings, then shouldn't ITP be disabled and the access is given?

I've added sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin" However, access was still not granted.

The iframe on the sample app I provided is a direct child of the webpage.

Yeah, I think you are right. It could be unrelated to ITP and be related to other cookie bugs on the link you provided. After doing some more tests I will reply back. Thank you!

mkayander commented 1 year ago

Hello. Any success with this issue? I'm also having problems with iframe cookies not being saved & used.

abhinavms commented 1 year ago

@mkayander For me by default, the Cookie policy is set to NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain which is preventing the cookies to be set even when cross-site tracking is enabled. Changing it to NSHTTPCookieAcceptPolicyAlways fixed the issue.

In CDVWebViewEngine.m, can you try adding the below line and see if that helps NSHTTPCookieStorage.sharedHTTPCookieStorage.cookieAcceptPolicy = NSHTTPCookieAcceptPolicyAlways;

mkayander commented 1 year ago

@abhinavms Thanks for the info! I've tried adding that line, but with no success yet. Is there a specific place in the file that this line should be at? I've currently added it inside the "initWithFrame".

abhinavms commented 1 year ago

In my codebase, it was added under "pluginInitialize". But I think it should have worked under initWithFrame also. Maybe you can debug and check the "cookieAcceptPolicy" value in your case. If it is already NSHTTPCookieAcceptPolicyAlways, then you might be facing a different issue.

Also just to confirm you have already added "NSCrossWebsiteTrackingUsageDescription" and enabled tracking in the app settings?

mkayander commented 1 year ago

Yeah, i've logged the cookieAcceptPolicy value and it's already 0 before the assignment, which is NSHTTPCookieAcceptPolicyAlways. NSCrossWebsiteTrackingUsageDescription is set and enabled in settings yes.

Yet, i see in dev tools that iframe's response contains multiple Set-Cookie, SameSite is set to none. But when i check saved cookies for iframe's domain, there's none, which results in errors.

I'm also using a cordova-plugin-wkwebview-file-xhr plugin that affects XHR requests but it doesn't seem to be relevant to the problem.

mkayander commented 1 year ago

As it turned out, those iframe cookies are only being saved if there's already a cookie present for this specific domain. I've used a cordova-plugin-wkwebview-inject-cookie plugin to add a dummy cookie for the required domain on the app startup, and it started working.

So it seems like a working solution for the test environment, but not a good option for the prod, since it's not great to force users to enable tracking in settings.

It looks like the only option for the production env is to make a separate build for each domain and enforce/proxy all iframes to it.