Problem/Motivation

The example $cookie_domain variable in settings.php fails for sites with subdomains and does not meet RFC 2109 standards. When $cookie_domain contains only one dot, each subdomain like http://de.example.com and http://fr.example.com issues its own session cookie to the user which prevents the session remaining active when switching between domains.

Current implementation:

$cookie_domain = 'example.com';

Correct version:

$cookie_domain = '.example.com';

To reproduce the problem:
1) Create two Drupal sites with subdomains (Example: http://de.example.com and http://fr.example.com).
2) Log into one of the sites.
3) Switch to the other site. You will be logged out.

Proposed resolution

The proposed solution is to add a period before the domain name for example.com and to add documentation that requiring a preceding dot meets RFC 2109 standards.

$cookie_domain = '.example.com';

Users who cannot apply the patch should add a preceding . to their $cookie_domain.

Remaining tasks

  • Commit to Drupal 8
  • Backport to Drupal 7
  • Backport to Drupal 6

User interface changes

No changes.

API changes

No changes.

Original report by reglogge

In settings.php the site administrator can define a $cookie_domain to ensure that users remain logged in when switching between several subdomains that follow the pattern http://de.example.com, http://fr.example.com and http://en.example.com. This is mostly used when running a multilingual site with domain prefixes.

This only works when $cookie_domain contains at least two dots '.' however. So setting $cookie_domain to
$cookie_domain = 'example.com';
as it is offered in settings.php as an example doesn't work.

The culprit seems to be this code in bootstrap.inc (lines 604ff.)

  // Per RFC 2109, cookie domains must contain at least one dot other than the
  // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
  if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
    ini_set('session.cookie_domain', $cookie_domain);
  }

where we check whether there are at least two dots in $cookie_domain.

I'm not sure how to fix this. Either we could eliminate the check for at least two dots or modify the comment and example in settings.php so that it reads something like this:

/**
* Drupal automatically generates a unique session cookie name for each site
* based on on its full domain name. If you have multiple domains pointing at
* the same Drupal site, you can either redirect them all to a single domain
* (see comment in .htaccess), or uncomment the line below and specify their
* shared base domain. Doing so assures that users remain logged in as they
* cross between your various domains. Make sure to always start
* $cookie_domain with a leading dot.
*/
# $cookie_domain = '.example.com';

Since RFC 2109 states on page 4 that "An explicitly specified domain must always start with a dot", it seems that amending settings.php is the safer route. Patch attached.

I'm setting this to major since this makes multilanguage sites pretty unusable when using domain prefixes.

Files: 
CommentFileSizeAuthor
#24 cookiedomain-doc-1005570-23.patch759 bytesreglogge
PASSED: [[SimpleTest]]: [MySQL] 33,644 pass(es).
[ View ]
#20 cookiedomain-doc-1005570-20.patch742 bytesreglogge
PASSED: [[SimpleTest]]: [MySQL] 33,577 pass(es).
[ View ]
#15 cookie_domain-1005570-15.patch2.67 KBreglogge
PASSED: [[SimpleTest]]: [MySQL] 29,727 pass(es).
[ View ]
#11 cookie_domain_5.patch1.72 KBreglogge
PASSED: [[SimpleTest]]: [MySQL] 31,523 pass(es).
[ View ]
#3 cookie_domain_4.patch1.73 KBreglogge
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch cookie_domain_4_0.patch.
[ View ]
#2 cookie_domain.patch924 bytesreglogge
PASSED: [[SimpleTest]]: [MySQL] 30,358 pass(es).
[ View ]
cookie_domain.patch925 bytesreglogge
PASSED: [[SimpleTest]]: [MySQL] 30,392 pass(es).
[ View ]

Comments

Status:Active» Needs review

bot...

StatusFileSize
new924 bytes
PASSED: [[SimpleTest]]: [MySQL] 30,358 pass(es).
[ View ]

To clarify: When $cookie_domain contains only one dot, each subdomain like http://de.example.com and http://fr.example.com issues its own session cookie to the user which prevents the session remaining active when switching between domains.

The attached patch removes a whitespace issue against the patch in the OP.

StatusFileSize
new1.73 KB
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch cookie_domain_4_0.patch.
[ View ]

This patch adds an additional check for the leading dot for a user-specified $cookie_domain in bootstrap.inc. It adds a dot if it has been omitted by the user. Not sure if this is babysitting though.

With this patch, the user could specify either example.com or .example.com and the cookie domain would always be set to .example.com which is the correct value.

For IP-addresses or domains not containing at least one dot in the middle of them (like localhost or .localhost) no cookie domain is set.

There is quite a bit of discussion around this here #458704: Don't automatically remove "www." from admin-set cookie domains, but that issue is currently against Drupal 6.x-dev.

Title:$cookie_domains in settings.php with less than two dots don't workSetting $cookie_domain in settings.php does not work as documented -> language negotiation with domain prefixes fails

Just clarifying the title.

I understand this could be a problem. This is something I came across with a multilingual site of mine. I had no idea I had to add a leading dot. It would be great to get this committed, so that others don't get exposed to the same problem.

It would be great to get this into 7.1, as default.settings.php already has a change for this file (removing typo). As it is now, this issue could really create confusion for multi-lingual site administrators. The easy fix would be to just change the comment documentation in default.settings.php, but adding a conversion in bootstrap.inc would also help a lot for those users who don't read comments.

Same issue in Drupal 6.x since upgrade to 6.17

#comment without . in example variable is present also in current 6.20 release

#3: cookie_domain_4.patch queued for re-testing.

Status:Needs review» Needs work

The last submitted patch, cookie_domain_4.patch, failed testing.

default.settings.php has changed in HEAD since this last patch was made. A new patch needs to be rolled.

Status:Needs work» Needs review
StatusFileSize
new1.72 KB
PASSED: [[SimpleTest]]: [MySQL] 31,523 pass(es).
[ View ]

Rerolled patch attached...

For me there is one more issue since upgrading from 6.16 to 6.20. I did use the .example.com but what happens it when i am logged in as user 1 when i switch languages user gets logout every time, like cookie is set only for one language. I'll opetn a separate issue for this.

This patch adds an additional check for the leading dot for a user-specified $cookie_domain in bootstrap.inc. It adds a dot if it has been omitted by the user. Not sure if this is babysitting though.

Setting $cookie_domain is an advanced feature, so we don't need to babysit too much. But if we can make it blend in with the existing code-path, I guess it's fine. E.g. by ltrim'ing the dot to begin with and then only add the leading dot directly in the ini_set('cookie_domain', ...) call. Perhaps replace explode(':', $cookie_domain) with strtok($cookie_domain, ':') to skip the following line.

A small cleanup is to replace to count(explode(...)) with substr_count().

Using ord($cookie_domain) !== 46 to check for a leading period is very obscure :-)

Version:7.x-dev» 8.x-dev
Issue tags:+needs backport to D7

StatusFileSize
new2.67 KB
PASSED: [[SimpleTest]]: [MySQL] 29,727 pass(es).
[ View ]

I rerolled the patch with a more conventional way (using substr()) of determining if the user-defined $cookie_domain starts with a period.

Other changes:

  • If a user sets a correct $cookie_domain in settings.php (with a leading period), the leading period is trimmed when setting the session name. This is the same behaviour as when Drupal determines the session name automatically.
  • I removed the trimming of the leading period when autocreating $cookie_domain from $_SERVER['HTTP_HOST'] because there will never be one (I think).
  • The check for the leading period (and adding it if it doesn't exist) now takes place only once, just before calling ini_set().
  • ini_set is called only if $cookie_domain contains at least 2 periods (determined with substr_count() as per the suggestion in #13).

The result of this is that $cookie_domain will now always be set with a leading period as mandated by RFC 2109. The table shows what happens if a user defines $cookie_domain in settings.php or $cookie_domain is auto-created by Drupal.

$cookie_domain in settings.php Autocreated from $_SERVER['HTTP_HOST'] Resulting $cookie_domain
example.com .example.com
.example.com .example.com
www.example.com .www.example.com
www.example.com .example.com
example.com .example.com
dev.example.com .dev.example.com
localhost not set
127.0.0.1 not set

Status:Needs review» Needs work

+  if (substr($cookie_domain, 0, 1) !== '.') {
+    $cookie_domain = '.' . $cookie_domain;
+  }
Is the "if" necessary? Aren't we sure that the domain never has a leading period, so that it must always be added?

- * between your various domains.
+ * between your various domains. Make sure to always start $cookie_domain with
+ * a leading dot.
Since we now ltrim() the period if it is there, it doesn't matter whether there is a leading dot or not, so the text “Make sure to ...” is a bit misguiding. But it is probably a good idea to mention one format just to prevent that users have to go elsewhere to look it up, e.g. by adding “(with a leading period)” after “specify their share base domain”. Well, just an idea.

      $cookie_domain = explode(':', $cookie_domain);
      $cookie_domain = $cookie_domain[0];
This is not related to your patch, but perhaps you could change it. I think it is bad code style that the $cookie_domain variable is used to hold a string, then it is reused to hold an array, and then again it uses a string. This can be avoided e.g. by replacing $cookie_domain = $_SERVER['HTTP_HOST']; with $cookie_domain = strtok($_SERVER['HTTP_HOST'] ':');

Oh, one other thing (outside the scope of this issue, so feel free to ignore this):
The condition !is_numeric(str_replace('.', '', $cookie_domain)) only works for IPv4 addresses. I suggest that we celebrate World IPv6 Day by adding support for IPv6 too, e.g. by changing the check to filter_var($cookie_domain, FILTER_VALIDATE_IP)).

#17 (adding IPv6 support) is a separate issue.

I think I ran into this issue a couple of times in the past years, so I'm tempted to demote this issue to normal.

The changes to settings.php and $cookie_domain look bogus to me. That said, the docs about $cookie_domain could use some love, indeed.

Title:Setting $cookie_domain in settings.php does not work as documented -> language negotiation with domain prefixes failsDocument leading dot requirement for $cookie_domain in settings.php
Priority:Major» Normal

This really looks like just a documentation issue to me, demoting from major since like sun says it's an advanced feature. Don't see any reason to add code to catch domains missing the dot really.

Status:Needs work» Needs review
StatusFileSize
new742 bytes
PASSED: [[SimpleTest]]: [MySQL] 33,577 pass(es).
[ View ]

Well then, let's just fix the documentation. Patch attached.

Status:Needs review» Reviewed & tested by the community

How about adding "Make sure to always start the $cookie_domain with a leading dot, as per RFC 2109." for all those wondering why. More eyes will be on this change than any other file in Drupal core (except for maybe .htaccess), as a change in default.settings.php can make them think that they may have to update their settings.php files accordingly.

Status:Reviewed & tested by the community» Needs work

Seems like a good suggestion.

Status:Needs work» Needs review
StatusFileSize
new759 bytes
PASSED: [[SimpleTest]]: [MySQL] 33,644 pass(es).
[ View ]

Here goes...

Status:Needs review» Reviewed & tested by the community

Looks good :)

Issue summary:View changes

adding issue summary

Issue summary:View changes

adding doc clarification

Version:8.x-dev» 7.x-dev

Committed to 8.x, moving to 7.x for webchick to consider.

Status:Reviewed & tested by the community» Fixed

I think this makes sense. People are going to whinge a bit about a change to default.settings.php, but the way it's written now is actually a bug.

Committed and pushed to 7.x.

Title:Document leading dot requirement for $cookie_domain in settings.phpChange notice needed for Document leading dot requirement for $cookie_domain in settings.php
Category:bug» task
Priority:Normal» Critical
Status:Fixed» Active
Issue tags:+Novice

And this actually seems worthy of a change notice.

Status:Active» Needs review

Change notice added at http://drupal.org/node/1325380.

Title:Change notice needed for Document leading dot requirement for $cookie_domain in settings.phpDocument leading dot requirement for $cookie_domain in settings.php
Category:task» bug
Priority:Critical» Normal
Status:Needs review» Fixed
Issue tags:-Novice

Looks good :)

Status:Fixed» Closed (fixed)
Issue tags:-needs backport to D7

Automatically closed -- issue fixed for 2 weeks with no activity.

Issue summary:View changes

Changed nameof original reporter to correct one.