10

I have this weird issue I cannot figure out, so I was hoping someone smarter than me could help!

I have a site https://example.com (no subdomain)

I have some code that sets a cookie, e.g.

var value="some value";
document.cookie="myCookie="+escape(value)+"; path=/; domain=example.com";

This code runs during page load in some script tag. When the page is first loaded, I see my cookie set, and in (chrome) dev tools > Application > Domain column, I see that it is set on .example.com with a leading dot, which is fine.

But then I refresh the page, and my code runs again (and there is a new value being pushed to the cookie; dunno if that matters). I look in the Application tab and I now see a "duplicate" entry for myCookie - one on .example.com and another on example.com (no leading dot). The values are the same. This is weird to me and I do not know why this is happening. Does anybody have any possible reasons for why this can happen?

Further down this rabbit hole.. I refresh the page again and the myCookie value on .example.com updates, but the one on example.com does not. More weirdness!

Meanwhile, I have other code that tries to read the cookie, but apparently the myCookie cookie on example.com takes precedence and I get that value, not the latest value (reflected in the .example.com cookie).

I have tried explicitly setting the cookie on .example.com (domain=.example.com) but the described behavior above still happens. Also, near as I can tell, there is no way for me in javascript to explicitly reference the cookie on .example.com - document.cookie just shows the example.com one (but the dev tools Applications tab does show it? So is this exposed in javascript?)

I can't provide a link to the site, but I can try to answer any questions someone might have to clarify.

But my main question is: can anybody explain possible reasons this might happen, or at least offer something that might point me in the right direction? Or failing that, alternatively some workaround to explicitly read from .example.com?

Edit

At this point, my best guess is something else on the site, likely server-side script, is duping the cookies over from .example.com to example.com but only if the values change. But this is pure speculation. I haven't been able to find any client-side proof of this (yet..) and I don't actually have access to server-side stuff. And this is probably grasping at straws anyways. But it's my best working theory ATM to take back to the site devs to ask them about..

Update - New stuff I have found / Clarifications

To be clear/restate: my site is on https://example.com with no submdomain.

This seems to only happen for my cookies where the value changes each page load. Cookies whose values do not change do not seem to get pushed from .example.com to example.com.

Even for the cookies with dynamic values, it only seems to dupe .example.com to example.com the first time. After that, the example.com cookie value does not change on subsequent page loads, even though the .example.com version gets a new dynamic value.

I have created a test script, separate from the cookies I currently observe this behavior, in an effort to divide and conquer. I thought to rule out any shenanigans with the code that sets/reads the cookies and blackbox this as much as possible.

But I am seeing the same behavior. I still suspect this is some kind of server wtfery going on, like the .example.com cookies are getting sent in the next http request and then the server is writing them back to example.com.. only on the first time? That's the double plot-twist. I don't have access to server-side stuff but I don't even know where to begin with what to holler at the site devs to look at.

Also note that so far between my colleagues and I, we tested the both the javascript lib/code that sets the problem cookies, as well as the blackbox script below on 6 other sites (not even owned/affiliated with this problem site at all) and have not been able to reproduce the issue, which further makes us suspect it is some kind of server config issue (but again, we don't really know where to start with that).

Anyways, in case it somehow helps anybody, below is the script I wrote to (further) test this issue. I am testing with a total of 8 cookies:

  1. test_no_dot_static - I set domain=example.com with a static value
  2. test_no_dot_dynamic - I set domain=example.comwith a dynamic value
  3. test_dot_static - I set domain=.example.com with a static value
  4. test_dot_dynamic - I set domain=.example.com with a dynamic value
  5. test_implicit_no_domain_static - I set a cookie with static value with no domain= specified
  6. test_implicit_no_domain_dynamic - I set a cookie with dynamic value with no domain= specified
  7. test_explicit_no_domain_static - I set a cookie with static value with domain= specified with no value
  8. test_explicit_no_domain_dynamic - I set a cookie with dynamic value with no domain= specified with no value

#1, #3 - These initially get set on .example.com and do not get duped to example.com on page refresh

#2, #4 - These initially get set on .example.com. On first page refresh, the original value is duped to example.com cookie/domain. On 2nd+ page refresh, the example.com cookie values do not change (retain original value), and the .example.com versions get new values each page load.

#5, #7 - These initially get set on example.com (not .example.com) and are there is never a version on .example.com.

#6, #8 - These initially get set on example.com (not .example.com) and their values update each page load, and there is never a version on .example.com .

Test Code

var s_testCookies = {
    getCookieValue: function(a) {
        var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
        return b ? unescape(b.pop()) : '';
    },
    getRandomValue: function() {
        return '' + (new Date()).getTime() + '|' + Math.floor(Math.random() * 100000);
    },
    run: function(data) {
        var data = data || {};
        console.log('------------------------');
        console.log('Arg: ', JSON.parse(JSON.stringify(data)));
        data.previousValue = s_testCookies.getCookieValue(data.name);
        console.log('Previous: ', JSON.parse(JSON.stringify(data)));
        data.cookie = data.name + "=" + escape(data.value) + "; path=/;";
        data.cookie+= (typeof data.domain!='undefined') && (" domain="+data.domain+";") || "";
        document.cookie = data.cookie;
        data.newValue = s_testCookies.getCookieValue(data.name);
        console.log('New: ', JSON.parse(JSON.stringify(data)));
        console.log('------------------------');
    }
} // end testCookies

s_testCookies.run({
    'message': 'no dot static value',
    'domain': 'example.com',
    'name': 'test_no_dot_static',
    'value': 'no dot static value'
});
s_testCookies.run({
    'message': 'no dot dynamic value',
    'domain': 'example.com',
    'name': 'test_no_dot_dynamic',
    'value': s_testCookies.getRandomValue()
});
s_testCookies.run({
    'message': 'dot static value',
    'domain': '.example.com',
    'name': 'test_dot_static',
    'value': 'dot static value'
});
s_testCookies.run({
    'message': 'dot dynamic value',
    'domain': '.example.com',
    'name': 'test_dot_dynamic',
    'value': s_testCookies.getRandomValue()
});
s_testCookies.run({
    'message': 'implicit no domain static value',
    'name': 'test_implicit_no_domain_static',
    'value': 'implicit no domain static value'
});
s_testCookies.run({
    'message': 'implicit no domain dynamic value',
    'name': 'test_implicit_no_domain_dynamic',
    'value': s_testCookies.getRandomValue()
});
s_testCookies.run({
    'message': 'explicit no domain static value',
    'domain':'',
    'name': 'test_explicit_no_domain_static',
    'value': 'explicit no domain static value'
});
s_testCookies.run({
    'message': 'explicit no domain dynamic value',
    'domain':'',
    'name': 'test_explicit_no_domain_dynamic',
    'value': s_testCookies.getRandomValue()
});

Another Update based on comments / answers provided

To be clear, in practice, the issue is that we have some cookies that we set with values on a page load. Then when the page is refreshed (or another page is navigated to on the site), we read the value, do something with it, generate a new value and set the cookies again. Wash rinse repeat each page (re)load.

The issue is on initial page view, those cookies are set on .example.com domain. Then on next page load, the initial cookie/value gets duped to exampe.com, and also the cookie on .example.com gets an updated value. Then on another page load, the previous .example.com value does not get duped/pushed to example.com version of the cookie; only the .example.com version updates. So it only gets duped/pushed to example.com on the 2nd page load. Not 3rd+.

Meanwhile, the example.com cookie takes precedence and is the one document.cookie returns when we parse for the cookie value. So on 3rd+ page load, we just keep getting the value from page #2 returned from that example.com cookie, instead of the updated value in .example.com

Meanwhile, we cannot simply not specify a domain= value (#6 or #8 above), because there are in fact subdomains in play e.g. mobile.example.com that also have the code that reads/updates/writes those cookies, and if we do not specify a domain, then the cookie is only available on exactly example.com and cannot be read on mobile.example.com.

Udpate

@ffeast asked the following questions:

What does curl -I yourdomain return? Are there any Set-Cookie headers?

Here you go!

user@host:~$ curl -I https://example.com
HTTP/1.1 200 OK
X-Powered-By: Express
set-cookie: site#lang=en; Path=/;Secure
set-cookie: connect.sid=s%3As46fwOPaosGdwis0G_Wdv7gzUs75Q0rr.O1gLsLmrHmmdBXxPWJrIq8UWwOOaQf96qxUlW%2FxnEuM; Path=/; HttpOnly;Secure
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Content-Type: text/html; charset=utf-8
Content-Length: 19632
ETag: W/"4cb0-bgHMEy79PzLMv7jhB1Lh6A"
Vary: Accept-Encoding
Date: Fri, 16 Mar 2018 20:42:19 GMT
Connection: keep-alive
Set-Cookie: SecureCookie=!zPdIdxiz3mOMe2MYzy1p873u0yWtMdduASsHoU06gkaLs306mhR7cb3ir4TA2EG2cQGKmeVWJeYvxA/flQfPQUixqL8WMOfROsTgu3UZZg==; path=/; Httponly; Secure

I also see the same Response Headers from browser tab.

So there are some set-cookie headers in the initial http response, yes. They are not cookies I personally have anything to do with or know about for my scope of things, but I can ask what they are for, if that somehow helps. Example:

I notice that a domain= value is not explicitly set for them.. is it possible this somehow sets a precedent for the browser, or am I barking up the wrong tree? Even if it did though, I don't think that explains the dupe to example.com only to my cookies that change their values, nor it only happening on 2nd page load (not 3rd+)? Not sure where you're going with this, but I'm all ears!

Rob
  • 26,989
  • 16
  • 82
  • 98
slinkhi
  • 947
  • 4
  • 16
  • 32
  • Having the same problem right now. I'm going to create a simple repro. Do you have any update? – Bruno Mar 14 '18 at 16:00
  • I figured out my problem. In my case I was wrong. In a case a was setting the cookie without explicitly passing the domain. – Bruno Mar 14 '18 at 16:54
  • The only update I have is I've had a lot more people look at the site and they see the same thing happen, but utter loss of explanation about how/why – slinkhi Mar 16 '18 at 02:29
  • did you try to delete the `myCookie` prior to setting it ? – ben Mar 16 '18 at 08:10
  • Did you check if your issue is reproductible on any other web browser (firefox, safari ?) – A. STEFANI Mar 16 '18 at 14:55
  • @ASTEFANI I have confirmed same behavior in Firefox. I do not have Safari to confirm (but will try to get it confirmed). Curiously, Edge does not seem to have a problem, because it seems to always set the cookie on `example.com` (no dot prefix) regardless of how I set the cookie, so there's only ever a single cookie on root `example.com`. I've been researching and found that IE (and I guess Edge too, as I have observed) have historically gone against the grain and strip the leading dot prefix to effectively make no distinction between `.example.com` and `example.com` – slinkhi Mar 16 '18 at 15:09
  • Did you try to use a function to set, get and check cookies ? – A. STEFANI Mar 16 '18 at 15:16
  • @ASTEFANI yes! see my update where we have also attempted to blackbox it – slinkhi Mar 16 '18 at 18:01
  • So not specifying any domain does not create conflicts of `.example.com`, right? – Munim Munna Mar 16 '18 at 18:17
  • @MunimMunna yes. However.. if you do not specify the domain, then the cookie will only work for exactly that domain. So for example we have the script running on other subdomains like `mobile.example.com`. If I do not specify the domain when setting cookie on `example.com` then it will only be scoped for `example.com` and `mobile.example.com` cannot access it. So simply not specifying a domain isn't actually an option – slinkhi Mar 16 '18 at 18:34
  • What does curl -I https://yourdomain return? Are there any Set-Cookie headers? – Oleg Kuralenko Mar 16 '18 at 19:27
  • @ffeast updated my post with response to your questions, thanks! – slinkhi Mar 16 '18 at 20:51
  • Since you already have the Chrome dev tools open, look under the Network tab and see if any page you're requesting is setting the cookie in its response headers. That's more reliable that the curl trick suggested by @ffeast, since it lets you see *exactly* what your browser is sending and receiving. – Ilmari Karonen Mar 18 '18 at 21:06
  • The question says **there is no subdomain** twice, then in "another update" section it says **in fact there are subdomains in play**!!! Can you simplify the question in 3 sections? like what I really want, what I have achieved so far, where am I stuck. – Munim Munna Mar 19 '18 at 02:04
  • @IlmariKaronen I did also look in the Network tab (which I noted in my update a the time). – slinkhi Mar 19 '18 at 23:46
  • @MunimMunna No, I said that the _solution/work-around_ to explicitly not set a `domain=` value was not an option because overall there are other subdomains the code is implemented on. Which has nothing directly to do with the actual problem at hand. – slinkhi Mar 19 '18 at 23:47
  • @MunimMunna also, I don't appreciate you making a lot of edits to my post. It has become very clear to me that you don't actually understand my issue. Please roll back all of your edits or else I will ask for site moderators to do so. – slinkhi Mar 19 '18 at 23:51
  • Where did I say do not set domain value? It has been clear to me, you are not reading my answer. Edit the question yourself where you think I have altered it misunderstanding you. But keep the first few paragrapghs so people can understand, people obviously overlook essay size unorganised question. Thats why noone other than me is showing interest. Got it? – Munim Munna Mar 20 '18 at 02:18
  • I didn't say _you_ said to do that workaround. I was responding to your comment about no subdomains vs. subdomains in general, because _you_ were confused about why subdomains were not mentioned, and then mentioned later. The were not originally mentioned because _they are not relevant_. The _only_ reason they were mentioned (later), was because someone (not you) mentioned a workaround to not set `domain=`. So I explained why that wasn't an option. – slinkhi Mar 20 '18 at 17:00
  • I will agree with you that most people skip TL;DR's, and again, I appreciate that you've been trying to help. I think your posted answer is a good starting point to look into, but I have repeatedly tried to explain and show how the points in your answers are dead ends and that I had already explored them (and even explained that in my post to begin with, before you even posted your answer). I feel like pointing the finger at each other saying "you don't understand" is not productive, and in general, I think I've pretty much given up hope on getting this thing solved here at this point. – slinkhi Mar 20 '18 at 17:04
  • It is extremely impolite to edit posts for anything more than pure clerical stuff (fix typoes, markup, etc.). If you don't understand something, ask for clarification. Or if you think others won't understand, provide advice. But don't presume you know better than someone to put words into their mouths. _Especially when they repeatedly tell you they don't think you understand their problem._ – slinkhi Mar 20 '18 at 17:13
  • And then tell them to edit it back themselves, and then tell them what they can and can't edit on their own question. Who do you think you are? This isn't a public wiki. This is _my_ question. Let _me_ stand or fall on it. Thankfully, I do not encounter many people like you on SO. – slinkhi Mar 20 '18 at 17:17
  • Have you checked my updated answer, I hope your issue is solved now. – Munim Munna Apr 10 '18 at 09:28

2 Answers2

2

If your goal is to use a cookie sharing between all sub-domains, allowing everyone to access and set the cookie, then you need to specify the root domain whenever you set the cookie.

Suppose you have sub.example.com, sub2.example.com and example.com, and you want to share the cookie between them then set the domain to example.com. When you don't specify a domain the cookie is only property of example.com or sub.example.com, but when you specify domain to be example.com, then it becomes accessible to example.com and all it's subdomains to read and write to.

domain=domain (e.g., 'example.com' or 'subdomain.example.com').
If not specified, defaults to the host portion of the current document location (but not including subdomains). Contrary to earlier specifications, leading dots in domain names are ignored, but browsers may decline to set the cookie containing such dot . If a domain is specified, subdomains are always included.
[source: developer.mozilla.org]

In example.com:

var value="some value";
document.cookie="myCookie="+escape(value)+"; path=/; domain=example.com";

In sub.example.com: (same as above)

var value="some value";
document.cookie="myCookie="+escape(value)+"; path=/; domain=example.com";

In both cases you have to set it to example.com. Otherwise the following situations may arise

  • In example.com if you forget to set example.com at a single place, a new cookie will be created only for example.com that will be different from the shared one and will not sync with it creating lots of confusion.
  • In sub.example.com if you forget to set example.com at a single place, a new cookie will be created only for sub.example.com that will be different from the shared one and will not sync with it creating lots of confusion.
  • You should not set cookie to .example.com as it is not appreciated according to the above quote.

Possible causes of cookie duplication:

  1. When first time the page is loaded JavaScript sets the cookie with specifying domain=example.com that sets the cookie for .example.com, when the second request is made browser sends the cookie to the server, some code on server-side receiving the new cookie sets it back on example.com. This can only be verified inspecting the response headers of the second request.
  2. When first time the page is loaded JavaScript sets the cookie with specifying domain=example.com that sets the cookie for .example.com. When the page loads for the seconds time any JavaScript code is by mistake setting it without specifying domain=example.com that sets the cookie for example.com. This can only be verified inspecting the JavaScript codes involved in setting cookies.

I believe accidental creation of domain/sub-domain specific cookies are the cause of all the anomalies you described. Reviewing every section of code where cookies are being set may solve the issue.

UPDATE: What is really happening here.

You are using InvocaJS v3.6.6 and AngularJS v1.2.19, both of them having cookie management features. InvocaJS stores data in cookie as invoca_session and specifies domain example.com, so the cookie is set for .example.com. There is no problem when AngularJS sees the cookie for the first time, it remembers it. AngularJS keeps a copy of all the cookies and when you update any cookie by AngularJS, you actually modify the copy. Afterward AngularJS loops through all the cookies, compares it with copy to determine and update which cookies have been updated by AngularJS.

InvocaJS the repeatedly changes the value of invoca_session. When AngularJS loops though all the cookies and invoca_session does not match with the value AngularJS has, it thinks the value has been changed by AngularJS (as described above) and it updates the value of invoca_session replacing the new value with old one. (I tested it, this is exactly how AngularJS v1.2.19 worked)

The problem is Angular v1.2.19 does not specify any domain while setting cookies (predicted earlier as bug probability #2). So a new cookie is created with domain set to example.com. Afterwards when InvocaJS tries to read the cookie it reads the old cookie set for example.com.

AngularJS altering cookie

Dev-tools Debugger

Dev-Tools Debugger 2

Munim Munna
  • 17,178
  • 6
  • 29
  • 58
  • I wasn't the one who downvoted.. but just throwing it out there, you can set cookies in js to other than what exactly matches your domain, as long as it is on a path that matches it. e.g. If I am on `www.example.com` I can definitely do `domain=example.com` ! – slinkhi Mar 16 '18 at 18:36
  • The root domain is the same, but for example, if I am on `www.example.com` and set `domain=www.example.com`, I cannot go to `example.com` and read that cookie. – slinkhi Mar 16 '18 at 18:41
  • anyways, not setting the domain is not an option. I have observed this issue on `example.com` but I cannot simply not specify a domain because it will make the cookie only accessible on exactly `example.com` Not setting the domain would sidestep my issue, but doesn't actually answer why it is happening in the first place, and would introduce a different issue in that I do indeed need subdomains to access the cookies – slinkhi Mar 16 '18 at 18:44
  • 1
    I do not think that is the issue. As mentioned, they are initially set and show `.example.com` (w/ dot prefix) as the domain, and i can indeed access them from e.g. `mobile.example.com` and on each page refresh, cookies with static values don't get duped. But cookies with updated values do get duped - on first refresh (but not subsequent). – slinkhi Mar 16 '18 at 18:59
  • and ultimately, the issue is because of this, on subsequent page views when i'm looking for an updated cookie value, the `example.com` cookie takes precedence and is returned from parsing `document.cookie` and like i said, it only gets a value on first refresh, not subsequent. – slinkhi Mar 16 '18 at 19:01
  • Okay so, I understand how cookie domains are supposed to work in general. My issue is not about ensuring cookie is set with a proper domain value. I feel I have more than clearly laid out the issue and what I've tried, showing me explicitly setting the cookie with different domain values, and how the *actual issue at hand* is that *only* on 2nd page view, and *only* for cookies whose values change, the cookies are getting duped from `.example.com` to `example.com` . I mean no offense and I really am grateful for you trying to help, but I'm starting to feel like you don't understand my issue :( – slinkhi Mar 16 '18 at 21:29
  • And I super apologize if that is because of my failure to communicate the problem, so please let me know if there is anything else I can clarify – slinkhi Mar 16 '18 at 21:30
  • okay, but in my post, one of the scenarios (#2) i showed exactly what you are showing to do and how i still see the same issue happening – slinkhi Mar 16 '18 at 22:12
  • I have also shown that I blackboxed the issue to rule out the possibility the real script is accidentally / occasionally setting the wrong domain value (your other points). – slinkhi Mar 16 '18 at 22:18
  • I pinky swear I really appreciate your help; I'm just explaining that I have covered these bases! – slinkhi Mar 16 '18 at 22:19
  • @slinkhi, We can continue the conversation [in this discussion](https://chat.stackoverflow.com/rooms/166994/discussion-between-munim-munna-and-slinkhi) as I need some more information. – Munim Munna Mar 17 '18 at 07:32
  • **Your #1 possible cause** - This is what I suspect, too. What throws me off though is that a) it only happens for cookies whose values change the 2nd page view, b) it *only* happens on the 2nd page view, not 3rd+. So I think this is probably what is happening, but I cannot figure out why `a` and `b` are happening. **Your #2 possible cause** - I am absolutely positive at this point this is not the case, from my blackbox script testing. – slinkhi Mar 17 '18 at 18:07
  • **For possibility #1:** I think it will better to check the response headers first without reasoning why. **For possibility #2:** Your black-box does not simulate this case, perhaps you misunderstood the case. I suggest trying it too. I have tested possibility #2 and it resembles your case and most probably this is the case. I suggest looking into the code and check if `domain=example.com` is specified everywhere or not. – Munim Munna Mar 17 '18 at 18:39
  • **possibility #2:** as I have mentioned in my post and confirmed several times in comments already, I have already verified that my code does not "accidentally" improperly set the domain. And part of the point of the blackbox script was to prove that this issue happens even with no other moving parts in play. Please actually look at the blackbox script and confirm for yourself the impossibility of it "accidentally" setting the wrong domain (prove me wrong! show me a flaw in that script!). And yet the cookie gets duped all the same. Please re-read my post and updates. – slinkhi Mar 19 '18 at 23:43
  • Again you forgot to mention what happened to possibility #1. And can you enlighten me about the blackbox script? Which pluggin that script is for? – Munim Munna Mar 20 '18 at 02:51
  • I am awarding you the bounty. But I want to make it very clear i am not awarding it to you because you have been helpful. In fact, none of your answer is anything I didn't already try or consider, and I've repeatedly tried to explain that to you. Worse, not only did you not understand that, but you decided I don't understand my own question and decided to edit my question. No, I am not awarding the bounty to you because you in any way helped solve my issue (which remains unsolved). I am _only_ awarding it to you because you tried to help, and I thank you for that. – slinkhi Mar 20 '18 at 17:23
  • @slinkhi I am still willing to help, I called you for discussion (you can see above comments), this is the first time I tried to help someone but failed because of communication. Join [this discussion](https://chat.stackoverflow.com/rooms/166994/discussion-between-munim-munna-and-slinkhi). We can still solve the problem or find any way out. – Munim Munna Mar 20 '18 at 17:31
-1

According to w3schools.com documention, you may use a function to built, get and check your cookie.

You may take a look on How do browser cookie domains work, also here and here to better understand how browser and javascript handle cookie setup.


As you said in your question that error is visible also Firefox, it may be useful to note that:

Contrary to earlier specifications, leading dots in domain names are ignored, but browsers may decline to set the cookie containing such dot . If a domain is specified, subdomains are always included.


As you said :

Meanwhile, the example.com cookie takes precedence and is the one document.cookie returns when we parse for the cookie value. So on 3rd+ page load, we just keep getting the value from page #2 returned from that example.com cookie, instead of the updated value in .example.com

You may double check if some data are loaded from a sub-domain xxx.example.com on the first load and future page load.

Verify that if some data are cached during future reload.

You may finally double check the presence of the following headers in requests:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

which avoid caching issue.

A. STEFANI
  • 6,707
  • 1
  • 23
  • 48
  • The cookies in question have a value that needs to be updated based on site logic. In practice, they are "previous page value" cookies that get read and used, and then an updated value is pushed to the cookies to be read on next page load. – slinkhi Mar 16 '18 at 18:04
  • (p.s. i'm not the downvote fairy. I appreciate all the help i'm getting!) – slinkhi Mar 16 '18 at 18:45