Sharing cookies with cross-origin iframes
Let me start with the background: We have an old project with many modules, using a rather outdated tech stack, making maintenance difficult.
So I planned to migrate some pages from the old project to a new one, and then integrate them together via iframes.
I learned that this project is a backend system not open to the public, so security requirements are not extremely high. When introducing the iframe, I simply put the token in the src as a query string. Then the new project retrieves the token from the URL and stores it in a cookie. This achieves login state synchronization.
Problem encountered
But after deploying the new project to the test environment, I found that all API requests from the new project failed authentication.
The project’s authentication is based on cookies, and requests made inside the iframe were not carrying cookies.
Investigation process
Because it’s cross-site, due to the blocking of third-party cookies, pages inside the iframe cannot read or write cookies.
Solution
Since cookies cannot be sent, I could add custom fields in the request headers.
I added a custom field in the request header, and then in Nginx forwarding, I renamed the field to Cookie, thus achieving identity masquerading and login state synchronization.
But this approach didn’t seem ideal.
Deploying under the same top-level domain makes it same-site. By default, the browser’s SameSite for cookies is Lax (explained below), so pages inside the iframe can write cookies.
Resource sharing
Translation and summary of external resources:
The correct way to write third-party cookies is as follows:
Set-Cookie: session=your_session; SameSite=None; Secure
Principle
When the domain of a cookie matches the domain of the website, it is called a first-party cookie; otherwise, it’s a third-party cookie. For a long time, both worked the same way — they were sent with requests. But this had some drawbacks:
Adding a lot of useless content in request headers
Being maliciously used to track user activity across multiple sites
Therefore, a new attribute, SameSite, was added to the cookie. This attribute allows you to declare whether your cookie should be restricted to first-party or same-site contexts. Possible values:
None: The cookie will also be sent in third-party contexts.
Strict: The cookie will only be sent in first-party contexts (when the domain in the browser’s URL bar matches the cookie’s domain).
Lax: The cookie will be sent in first-party contexts and also along with top-level navigations.
The browser’s default behavior changed. Previously the default was None, now it is Lax. To set it to None, it must be in a secure context, i.e., https.
Additionally, the Secure attribute must be set when using Set-Cookie, meaning the cookie can only be transmitted over HTTPS.
Why not
Why not have the new project send its own request to obtain a token and then
Set-Cookie? Because this would cause the old project’s login session to be invalidated.Why not deploy on the same origin? Because people are more complex than code, and communication costs are relatively high.
Why not use the correct method mentioned below? Because deploying under the same top-level domain requires no code changes.
Afterword
"English is just another programming language."
— The Pragmatic Programmer: Your Journey to Mastery (2nd Edition)
Sometimes, due to poor communication, I come up with many clever tricks to try to solve problems.
Even if a temporary fix works, because it’s not the correct solution, I still feel uneasy. So it’s important not to just focus on writing code.