Multiple cookies with same name after changing the sameSite setting - node.js

Context:
In our Node.js & express based app I updated the cookie settings from
res.cookie(newCookieName, sessionCookie, {
domain: getCookiesDomain(),
maxAge: ms('30 days'),
secure: true,
httpOnly: true
});
To this slightly different options by adding explicitly sameSite property:
res.cookie(newCookieName, sessionCookie, {
domain: getCookiesDomain(),
maxAge: ms('30 days'),
secure: true,
httpOnly: true,
sameSite: 'lax'
});
And as result, I see 2 values of cookies stored at browser, probably due different sameSite values ("default" and explicitly set lax value).
Browser: Chrome 102, OS: Windows 11.
Who else experienced this double-cookies at case of updated cookie settings?
Update
Possible cause is that at least Chrome (or may be other browsers as well?) threat different configuration of these flags and options like HttpOnly, Secure or SameSite as part of cookie full identity, among with origin and name of cookie.
It is possible to mitigate this problem by these tactics:
if you use some different format in new version of cookie, like s:value.signature in old cookie and prefix.JWT_base64url you will have to scan all arriving cookie values by parsing manually arriving Cookie header to be able detect this case. Standard Express cookie parsing code for express-session useless in this tricky scenario.
The second part is to clear manually cookies with deprecated/legacy options like Secure, HttpOnly and absent or different SameSite values. So if you did not specify SameSite, than later used Lax value and finally settled on Strict value, you will have 3 separate cookies, so you will need 2 cookie cleanup commands (2 additional set-cookie headers) set with response to clear these old versions of cookies.
Of course you can add custom properties to Request in express to mark request with flags mentioning whether such extra headers should be sent with response for request where multi-cookie issue was detected to send extra cleanup set-cookie headers with empty value and negative maxAge in res.cookie cleanup call only when such custom flags present on request, if you use this tweak.
Note that standard res.clearCookie provided by Express unaware of this issue and you can not provide the related options there, as I know.

Related

Cookie is not set on Production when I add domain option when setting cookie

I host my application on vercel.com. At first the cookie is not working at all, I tried to add the options in the cookie by many solutions like
{
path: '/',
secure: true,
sameSite: 'none'
}
and it works but unfortunately, it appeared as the cookie is not shown in the browser, but on the api the cookie is set which made me confused. So I thought maybe I forgot to add domain option when setting cookie, then I added domain in the cookie option like:
{
domain: 'mydomain.me'
path: '/',
secure: true,
sameSite: 'none'
}
Then the cookie is not working again and I when I checked in Network it said, this attempt to set a cookie via set-cookie header was blocked because its domain attribute was invalid with regards to the current host url.
What really went wrong here, why the cookie is not set when I add domain option?
And when I did not add domain why the cookie is set but not showing on the browser?

Express clearCookie does not work as expected with options supplied

The code to set session cookie is following:
res.cookie(newCookieName, sessionCookie, {
domain: getCookiesDomain(),
maxAge: ms('30 days'),
secure: true,
httpOnly: true,
sameSite: 'lax'
});
On logout we do this:
function clearOneSessionCookie(res:Response, cookieName, sameSite?: 'lax'|'strict'|'none'):void {
if (sameSite) {
res.clearCookie(cookieName, {
domain: getCookiesDomain(),
maxAge: -1000,
httpOnly: true,
secure: true,
sameSite: sameSite
});
} else {
res.clearCookie(cookieName, {
domain: getCookiesDomain(),
maxAge: -1000,
httpOnly: true,
secure: true
});
}
}
clearOneSessionCookie(res, newCookieName);
clearOneSessionCookie(res, newCookieName, 'lax');
clearOneSessionCookie(res, newCookieName,'strict');
clearOneSessionCookie(res, legacyCookieName);
clearOneSessionCookie(res, legacyCookieName, 'lax');
clearOneSessionCookie(res, legacyCookieName,'strict');
we apply all possible options of clearOneSessionCookie because at various stages of our project moving to different cookie name and options, we used different sameSite options.
I even updated to latest express, cookie-parser packages in hope for fixing that, but no effect so far.
After logout requests, following information displayed at Cookies tab at Google Chrome:
Request Cookies:
old_cookie_name: domain = www.example.com, path = /, expires = Future_Date_1, HttpOnly = yes, Secure = yes, SameSite=[not set!]
new_cookie_name: domain = www.example.com, path = /, expires = Future_Date_2, HttpOnly = yes, Secure = yes, SameSite=Lax
Response Cookies:
old_cookie_name: domain = www.example.com, path = /, max-age: -1000 ms, HttpOnly = yes, Secure = yes, Same-Site: Lax
old_cookie_name: domain = www.example.com, path = /, max-age: -1000 ms, HttpOnly = yes, Secure = yes, Same-Site: Strict
new_cookie_name: domain = www.example.com, path = /, max-age: -1000 ms, HttpOnly = yes, Secure = yes, Same-Site: Lax
new_cookie_name: domain = www.example.com, path = /, max-age: -1000 ms, HttpOnly = yes, Secure = yes, Same-Site: Strict
Notice that there is now row like
old_cookie_name: domain = www.example.com, path = /, max-age: -1000 ms, HttpOnly = yes, Secure = yes, Same-Site: [not set]
So, it seems that call
clearOneSessionCookie(res, legacyCookieName);
Does not work or not understood/recognized by browser.
This specific set-cookie has been ignored:
set-cookie: old_cookie_name=; Max-Age=-1; Domain=www.example.com; Path=/; Expires=Sat, 04 Jun 2022 15:27:10 GMT; HttpOnly; Secure
What is the best way to clear a cookie with old name and without explicit Same-Site value applied?
Thanks for your answers and time.
Update:
Clarification of symptoms of problem:
The legacyCookieName cookie with not set (no value for SameSite attribute) as outcome the existing users who was signed in before migration to new cookie setting are unable to sign out of website.
Update
Even If I made a workaround for this problem (see marked answer), if someone would offer a better working solution while bounty is active, prize is yours.
After trying various tricks and tweaks to settings of legacy cookie removal API call, I came to conclusion that so far there is a single workaround for the bug being described:
add to responses a new cookie like authProtocolVersion, with value like 1 if session involved in request processing.
If this cookie was already present at stage of request arrival and request must interact with session, this will update the session cookie analysis behavior to skip/ignore legacySyssionCookie even if it still present, and newCookieName not present on request arrival.
still sending on logout cookie cleanup during logout the clearOneSessionCookie calls in all possible combinations of SameSite - not set, none, lax, strict, to make sure that eventually the legacy auth cookie will be cleared as soon as that bug with cookies cleanup will be fixed at browser side.
with the new chrome update
if you don't specify sameSite then it defaults to sameSite:'lax' by default.
so if you set a cookie with sameSite:'lax' and clearCookie with no SameSite property then the cookie gets deleted. because no samesite is = sameSite:'lax'

Reactjs - cookies do not persist only in production, with Firefox giving incorrect warning

So, in my application I am setting a cookie as follows:
this.cookies.set('session', sessionProperties, {
path: '/',
expires: new Date(session.valid_until),
secure: true,
sameSite: 'none'
});
By debugging, I can see the correct string being generated, ending in SameSite=None; Secure. I then see the .cookies object with the session object in it.
This occurs only on the production server - it works locally and on other test servers.
However, the browser then reports that no cookies exist at all, and Firefox complains that the session cookies will in the future not save because it is missing Secure - which is absurd since (1) it is already not being saved, and (2) it does have Secure set
I have CORS set up correctly, allowing the correct origin domain and with Allow Credentials
I am fetching with credentials: true
It is HTTPS
I am out of ideas - I don't know why it is not being saved, or how to troubleshoot this further.
Please note that secure:true and sameSite: 'none' as well as credentials: true and non wildcard CORS are all troubleshooting steps taken while trying to fix this, and are not the cause since the issue predates them
The only thing I can see is that something between me and the server is causing cookies to fail.
What could cause this?

Overwrite / update browser cookie

I have an Express 4.x app, and I pass a cookie to the browser with
res.cookie('foo','bar1', {maxAge:99999999999});
it expires in the distant future. However, 5 minutes later, I get another request from the same user, and I want to give them a new cookie.
res.cookie('foo','bar2', {maxAge:99999999999});
From my debugging, it looks like the new cookie doesn't overwrite the old cookie? Is that the case? How can I update/overwrite the old cookie with the new one?
I am about 99% certain that using the {overwrite: true} property will do the trick - from here:
https://www.npmjs.com/package/cookies
it says:
cookies.set( name, [ value ], [ options ] ) This sets the given cookie
in the response and returns the current context to allow chaining.
If the value is omitted, an outbound header with an expired date is
used to delete the cookie.
If the options object is provided, it will be used to generate the
outbound cookie header as follows:
maxAge: a number representing the milliseconds from Date.now() for
expiry expires: a Date object indicating the cookie's expiration date
(expires at the end of session by default). ... overwrite: a boolean
indicating whether to overwrite previously set cookies of the same
name (false by default). If this is true, all cookies set during the
same request with the same name (regardless of path or domain) are
filtered out of the Set-Cookie header when setting this cookie.
So that would be, in Node.js Express parlance:
res.cookie('cdt_app_token', encoded, {maxAge: 90000000, httpOnly: true, secure: false, overwrite: true});
Note that if you are using a non SSL or non TLS connection, cookies may not work if secure is true.

express-session secure: true

app.use(session({
secret: "testing credentials",
store: sessionStore,
resave: true,
saveUninitialized: true,
cookie : {
httpOnly: true,
//secure: true,
maxAge : 60 * 60 * 1000
}
}));
I'm working on some security problems on my newly developed website. And after done some research online, if secure=true is set, then it will be more secure. However,
If set secure: true, then information inside session will lose every time when the user send another request. Is there a way to solve this problem? If doesn't include "secure: true" in the cookie: , then the session will last for that maxAge.
If a cookie is set with the secure flag, it will only be sent to the server by the browser over https, and not plain http. This should be the default for production environments.
However, when developing an app, you probably use plain http on your dev machine. If you set your session cookie as secure in this case (using plain http), the server will never receive it, and you will experience a new empty session on each request.
So in short, you should only set the cookie as secure if you are using https (that is, in later stages of your development pipeline, and definitely in production).
On another note, if you set maxAge, the cookie will be persisted, which is not the best practice for session cookies. Without maxAge, the cookie will be kept until the user closes the browser and not normally persisted to disk, which is the correct behaviour for session cookies.

Resources