5

My users are having trouble logging into my site in Internet Explorer (10 and 11, perhaps older).

Through some trial and error, I discovered that IE is setting the PHPSESSID cookie twice. This is visible by typing document.cookie into the F12 developer toolbar.

Research suggests that this may be caused by having a "www." version and non-www version of the cookie. I'm not sure how to verify this, but I was hoping I could boycott the issue by setting the domain explicitly by calling session_set_cookie_params.

Thus I have:

$host = $_SERVER['HTTP_HOST'];
if(substr($host, 0, 4) === 'www.') $host = substr($host, 4);
$colon = strrpos($host, ':');
if($colon !== false) $host = substr($host, 0, $colon);
session_set_cookie_params(0, '/', $host);
session_start();

I'm stripping the port too (developing on localhost:81) because that's what document.domain returns.

Host is now simply localhost (haven't tried on live site yet), and I can't log in at all in IE. Works fine in Firefox, does not work in Chrome.

How can I get PHP or the browser to share session cookies between the www subdomain and no subdomain versions of the site?


I tried throwing a dot in there per the related question:

$host = $_SERVER['HTTP_HOST'];
if(substr($host, 0, 4) === 'www.') $host = substr($host, 4);
$colon = strrpos($host, ':');
if($colon !== false) $host = substr($host, 0, $colon);
session_set_cookie_params(0, '/', '.'.$host);
session_start();

I dumped session_get_cookie_params() to confirm it was set correctly. This does not appear to solve my issue either. The session keeps getting reset.

mpen
  • 272,448
  • 266
  • 850
  • 1,236

1 Answers1

1

I wrote a function to parse out the cookie string manually, which handles dupes:

function parse_cookie_str($str = null) {
    if ($str === null) 
        $str = array_key_exists('HTTP_COOKIE', $_SERVER) 
               ? $_SERVER['HTTP_COOKIE'] 
               : ''
        ;

    $kvps    = array_map('ltrim', explode(';', $str));
    $cookies = array();

    foreach ($kvps as $kvp) {
        list ($name, $value) = explode('=', $kvp);
        if (array_key_exists($name, $cookies)) {
            if (!is_array($cookies[$name])) {
                $cookies[$name] = array($cookies[$name]);
            }
            $cookies[$name][] = $value;
        } else {
            $cookies[$name] = $value;
        }
    }

    return $cookies;
}

If PHPSESSID is in there twice, we know something's wrong. The cookie does not appear to be deletable, so let's use some social engineering:

$cookies = parse_cookie_str();

$phpsessid = session_name();

if (
    isset($cookies[$phpsessid]) 
    && is_array($cookies[$phpsessid])
) { // two or more PHPSESSIDs found
    exit("Your session is corrupt. Please close your browser and try again.");
}

Now that the PHPSESSID issue is fixed, I just need to force the users to one subdomain so that the issue doesn't come up again. This must be done before starting the session.

So far, this works.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 1
    Great man.. You saved my day. I didn't know what the heck was going on IE. – Sanoob Feb 12 '14 at 06:30
  • A bit this sounds you're fixing the problem on the wrong end. Instead of finding out why exactly IE is setting this cookie in a dorked way (and how to prevent it in the first place), you introduce code that fixes it in retrospect. You should perhaps add more detail to your question, for example how the cookie is transfered to IE over HTTP to be set and how exactly you're checking it (JS code and debug output) and also how IE transfers the cookie via HTTP to the server then again. – hakre Feb 16 '14 at 00:42
  • @hakre: Setting the domain on the cookie as per the linked question didn't work for me. The solution I posted here isn't a fix, it just detects the problem for any user whose session has already been fried. It's happening because when users first hit the site they're on the non-www version, but my code sets the session cookie immediately, before checking the subdomain. It then forces them over to www and then creates a 2nd session (in IE). Solution is simply to check the subdomain before creating the session. – mpen Feb 16 '14 at 23:52
  • @Mark: That part was clear to me, my suggestion was that you add the HTTP communication so that it is clear *why* or at least *when* IE inserts this borken cookie. Also what exactly IE sends to the server as cookie headers would be visible. This requires monitoring the wire, but it's possible via a proxy or with a network monitor. – hakre Feb 17 '14 at 07:44