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.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

reglogge’s picture

Status: Active » Needs review

bot...

reglogge’s picture

FileSize
924 bytes

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.

reglogge’s picture

FileSize
1.73 KB

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.

reglogge’s picture

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

Just clarifying the title.

Tor Arne Thune’s picture

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.

Tor Arne Thune’s picture

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.

tmk1’s picture

Same issue in Drupal 6.x since upgrade to 6.17

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

reglogge’s picture

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

Status: Needs review » Needs work

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

Tor Arne Thune’s picture

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

reglogge’s picture

Status: Needs work » Needs review
FileSize
1.72 KB

Rerolled patch attached...

Marko B’s picture

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.

c960657’s picture

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 :-)

Tor Arne Thune’s picture

Version: 7.x-dev » 8.x-dev
Issue tags: +Needs backport to D7
reglogge’s picture

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
c960657’s picture

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'] ':');

c960657’s picture

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)).

sun’s picture

#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.

catch’s picture

Title: Setting $cookie_domain in settings.php does not work as documented -> language negotiation with domain prefixes fails » Document 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.

reglogge’s picture

Status: Needs work » Needs review
FileSize
742 bytes

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

catch’s picture

Status: Needs review » Reviewed & tested by the community
Tor Arne Thune’s picture

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.

webchick’s picture

Status: Reviewed & tested by the community » Needs work

Seems like a good suggestion.

reglogge’s picture

Status: Needs work » Needs review
FileSize
759 bytes

Here goes...

Tor Arne Thune’s picture

Status: Needs review » Reviewed & tested by the community

Looks good :)

JuliaKM’s picture

Issue summary: View changes

adding issue summary

JuliaKM’s picture

Issue summary: View changes

adding doc clarification

catch’s picture

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

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

webchick’s picture

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.

webchick’s picture

Title: Document leading dot requirement for $cookie_domain in settings.php » Change 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.

reglogge’s picture

Status: Active » Needs review

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

Tor Arne Thune’s picture

Title: Change notice needed for Document leading dot requirement for $cookie_domain in settings.php » Document 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.

Anonymous’s picture

Issue summary: View changes

Changed nameof original reporter to correct one.

AlexBorsody’s picture

This is deprecated, maybe consider rewriting.

RFC 6265 section 4.1.2.3

For example, if the value of the Domain attribute is "example.com", the user agent will include the cookie in the Cookie header when making HTTP requests to example.com, www.example.com, and www.corp.example.com. (Note that a leading %x2E ("."), if present, is ignored even though that character is not permitted, but a trailing %x2E ("."), if present, will cause the user agent to ignore the attribute.)

davenok’s picture

So, we are trying to implement the GSA (Google Search Appliance) to crawl our Drupal site. However, we have not had any success with getting the device authenticated to the site. We have been working with google support engineer who has identified this as the issue.

Set-Cookie: SSESSf893ea1f149fa728663bc7c5088af4ea=kctTZiPh3LO9SAjOgxrbT8f1vPRql6SyWic41GVjivU; expires=Fri, 25-Mar-2016 23:33:35 GMT; Max-Age=2000000; path=/; domain=.subdomain.mydomain.com; secure; HttpOnly

Domain is specified with leading dot at the beginning. But, during GET request, GSA doesn't submit this cookie to the portal as it fails to match with the URL https://subdomain.mydomain.com/.

This is not an expected behavior and we already have a bug for it #6846787. Currently it's not fixed and the only possible workaround I can propose - change cookie domain to "domain=subdomain.mydomain.com" (w/o leading dot) on the portal side. It should resolve the issue.

However, if I modify my settings.php for the cookie_domain setting and leave the leading . out, I am unable to authenticate to the site at all.

Is there a way to change this behavior?