At wordpress.com we tried to enable the Partitioned attribute on some of our auth cookies and then had to quickly revert the changes because we discovered the following problem.
At jardasn.dev I prepared a minimal reproduction example that shows what's going wrong. After visiting jardasn.dev and clicking "Login" you'll see a page with two embedded iframe. This is how it looks like in Chrome (with blocked 3rd party cookies):
The site has a top-level login cookie called logged_in. And there are two iframes that embed a document from a same-site same-origin URL. One is embedded directly, the other is nested in an intermediate cross-origin iframe from jardasn-alt.dev.
When loading the jardasn.dev/embed document, the response includes a Set-Cookie header with another cookie, logged_in_rest. When the request has the logged_in cookie, then logged_in_rest value will be the same as logged_in, i.e., admin. When the request doesn't have logged_in cookie, the value will be anon. Both cookies are supposed to be in sync. The logged_in_rest cookie is partitioned, it's supposed to be "private" to the embed iframe. Think of it as an extra authentication token, or nonce, specific for the embed.
The second iframe is nested inside cross-origin jardasn-alt.dev iframe. That makes Chrome treat it as third party. Therefore the logged_in cookie will be blocked when the jardasn.dev/embed is loaded, and the iframe will not have storage access. The logged_in_rest cookie returned by the server is going to be anon.
The first iframe is logged in, the second is logged out. However, both iframes share the same partitioned storage! The second iframe loaded a bit later, and when storing the logged_in_rest=anon cookie, it set it also for the first iframe! If you click the "Reread document.cookie" button in the first iframe, you'll see that the value changes from admin to anon:
That's because the second iframe stored a new value. This is a big problem because instead of having one frame fully logged in and another fully logged out, the iframes pollute each other's cookies and the first frame becomes half logged out.
Let's send a REST request from both iframes. Clicking the "Send REST request" will trigger a same-origin fetch() and in the response the server will tell us which cookies did the request have:
The first iframe's request cookies are out of sync. logged_in (first party cookie) is admin, but the logged_in_rest is anon, like if the iframe was logged out.
The second iframe's request cookie are OK. The logged_in one is missing because it's a first party cookie and it's blocked because there's no storage access. And the partitioned logged_in_rest cookie also says the request is anonymous.
If the first iframe loaded second, it would lead to a similar situation where the second frame is broken. It wouldn't have a valid logged_in cookie available, but it would have a valid logged_in_rest=admin. The login information would leak into the logged out iframe!
The root cause of all this is that both iframes share the same partition key, while having different first-party cookie access.
Firefox behaves differently. (I use the Nightly version with optInPartitioning enabled) Apparently both cookies are stored with different partition keys because they have two independent values:
The first frame is fully logged in, the second is fully logged out, each has its own logged_in_rest cookie and clicking "Re-read document.cookie" doesn't change the value. Both REST requests have two cookies that are in sync.
Safari also behaves differently. It doesn't support partitioned cookies, but the key difference is that both iframes do have storage access, so they both see the same first party logged_in and logged_in_rest cookies:
It seems that Safari doesn't care about the intermediate cross-origin jardasn-alt.dev iframe and that it gives the nested iframe full storage access anyway.
It's only Chrome where the problematic situation happens: shared partitioned storage, different storage access.
The second iframe can "fix itself" by requesting storage access and immediately reloading:
if (!await document.hasStorageAccess()) {
await document.requestStorageAccess();
document.location.reload();
}
The iframe is already "entitled" to have storage access, it just has to ask for it. And the request is exempt from the usual limitations -- doesn't need to be triggered by user gesture, doesn't show a popup.
But anyway, while the iframe is reloading, the bad logged_in_rest cookie is still in the shared storage and can cause damage. Only after it reloads it sets the right cookie and both iframes are logged in.
At wordpress.com we tried to enable the Partitioned attribute on some of our auth cookies and then had to quickly revert the changes because we discovered the following problem.
At
jardasn.devI prepared a minimal reproduction example that shows what's going wrong. After visitingjardasn.devand clicking "Login" you'll see a page with two embedded iframe. This is how it looks like in Chrome (with blocked 3rd party cookies):The site has a top-level login cookie called
logged_in. And there are two iframes that embed a document from a same-site same-origin URL. One is embedded directly, the other is nested in an intermediate cross-origin iframe fromjardasn-alt.dev.When loading the
jardasn.dev/embeddocument, the response includes aSet-Cookieheader with another cookie,logged_in_rest. When the request has thelogged_incookie, thenlogged_in_restvalue will be the same aslogged_in, i.e.,admin. When the request doesn't havelogged_incookie, the value will beanon. Both cookies are supposed to be in sync. Thelogged_in_restcookie is partitioned, it's supposed to be "private" to the embed iframe. Think of it as an extra authentication token, or nonce, specific for the embed.The second iframe is nested inside cross-origin
jardasn-alt.deviframe. That makes Chrome treat it as third party. Therefore thelogged_incookie will be blocked when thejardasn.dev/embedis loaded, and the iframe will not have storage access. Thelogged_in_restcookie returned by the server is going to beanon.The first iframe is logged in, the second is logged out. However, both iframes share the same partitioned storage! The second iframe loaded a bit later, and when storing the
logged_in_rest=anoncookie, it set it also for the first iframe! If you click the "Reread document.cookie" button in the first iframe, you'll see that the value changes fromadmintoanon:That's because the second iframe stored a new value. This is a big problem because instead of having one frame fully logged in and another fully logged out, the iframes pollute each other's cookies and the first frame becomes half logged out.
Let's send a REST request from both iframes. Clicking the "Send REST request" will trigger a same-origin
fetch()and in the response the server will tell us which cookies did the request have:The first iframe's request cookies are out of sync.
logged_in(first party cookie) isadmin, but thelogged_in_restisanon, like if the iframe was logged out.The second iframe's request cookie are OK. The
logged_inone is missing because it's a first party cookie and it's blocked because there's no storage access. And the partitionedlogged_in_restcookie also says the request is anonymous.If the first iframe loaded second, it would lead to a similar situation where the second frame is broken. It wouldn't have a valid
logged_incookie available, but it would have a validlogged_in_rest=admin. The login information would leak into the logged out iframe!The root cause of all this is that both iframes share the same partition key, while having different first-party cookie access.
Firefox behaves differently. (I use the Nightly version with
optInPartitioningenabled) Apparently both cookies are stored with different partition keys because they have two independent values:The first frame is fully logged in, the second is fully logged out, each has its own
logged_in_restcookie and clicking "Re-read document.cookie" doesn't change the value. Both REST requests have two cookies that are in sync.Safari also behaves differently. It doesn't support partitioned cookies, but the key difference is that both iframes do have storage access, so they both see the same first party
logged_inandlogged_in_restcookies:It seems that Safari doesn't care about the intermediate cross-origin
jardasn-alt.deviframe and that it gives the nested iframe full storage access anyway.It's only Chrome where the problematic situation happens: shared partitioned storage, different storage access.
The second iframe can "fix itself" by requesting storage access and immediately reloading:
The iframe is already "entitled" to have storage access, it just has to ask for it. And the request is exempt from the usual limitations -- doesn't need to be triggered by user gesture, doesn't show a popup.
But anyway, while the iframe is reloading, the bad
logged_in_restcookie is still in the shared storage and can cause damage. Only after it reloads it sets the right cookie and both iframes are logged in.