phpsessionfirefoxgecko

PHP Session cookie randomly being nonsecure, fails to replace secure cookie, 90% all Firefox users afflicted


Catalyst:

Gecko Bug 1617361 is a severe cookie-related bug where Gecko browsers randomly refuse to send the HTTP Cookie header (either extremely difficult to reproduce or (for the layman) extremely difficult to escape (just clear cookies)). It was later revealed that Gecko does not report an error where nonsecure cookies were not able to overwrite secure cookies. However after changing the session cookie name from session to session2 and setting all the security/domain settings to FALSE Gecko is still refusing to send the HTTP Cookie header. This bug is severe enough that it afflicts 90% of all people's Gecko browsers effectively preventing them from loading websites powered by my platform. As someone fundamentally against monopolies I need to address this on all fronts.

Which Bugs are Which?

Question:

How would my PHP code below fail to set the session cookie as secure in any context of a request (on live (or "production") servers) and how would I rewrite the code to ensure that the session cookie is not only secure when it is set though always secure when it is reinitialized for subsequent page requests?

Error Message

The following is the error this post is directly about. This error message is only generated when in Firefox you go to about:networking#logging, set cookie:5 to "Current Load Modules" (comma delimiter) and log while the issue occurs hence the difficulty and frustration of not just one though multiple bugs. Here is the error message it generates:

W/cookie rejected because cookie can't save because older cookie is secure cookie but newer cookie is non-secure cookie

Details:

While there are two bugs relevant to this post the bug I'm trying to address is when my PHP code resumes the session but does not make the session cookie secure. In an attempt to address the issue for the original catalyst bug my server now uses setcookie('session2',session_id(),time()+3600,'/',$_SERVER['HTTP_HOST'], FALSE, FALSE); until both that bug and my own PHP code are all properly working.

Verification Requirements for Acceptable Answer

Here is the original PHP code that handled sessions:

<?php
if (substr($_SERVER['DOCUMENT_ROOT'],0,1) == '/' && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
{
 ini_set('session.cookie_secure', 1);
 ini_set('session.gc_maxlifetime', 3600);
 session_set_cookie_params(3600);
}
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_samesite','Strict');
ini_set('session.use_only_cookies', 1);
session_name('session');
session_start();
?>

Solution

  • This was a bit tricky but I hope I have found a solution for you. I assume (1) will solve your problem. Everything else is optional and should help you to reduce some potential issues.

    1. Add your domain name to the default cookie parameters when useing samesite=Strict

    I was able to reproduce your issue with your code snippet. My Opera browser was not showing the cookie and was ignoring them completely.

    Interestingly when trying a curl request on the CLI then the set-cookie header was available which started to puzzle me a lot.

    If you enable samesite=Strict then you MUST set your domain within session_set_cookie_params otherwise my browser was ignoring it.

    session_set_cookie_params(3600, '/', 'cookie-test.local');
    

    was fixing the issue for me.

    2. Your site sends the "set-cookie" header twice with different settings

    $ curl -I -X GET https://www.jabcreations.com/ | grep set-cookie
    set-cookie: session2=e633d6fcb7fb426f86b065173bc6c8ab; expires=Wed, 18-Mar-2020 20:05:21 GMT; Max-Age=3600; path=/; secure; HttpOnly; SameSite=Strict
    set-cookie: session2=e633d6fcb7fb426f86b065173bc6c8ab; expires=Wed, 18-Mar-2020 20:05:21 GMT; Max-Age=3600; path=/; domain=www.jabcreations.com
    

    I was able to reproduce this with the following code. My Opera-browser also show me the error inside the specific request's header-tab. Next to the cookie you'll see small yellow "hazard" icon. In my case it displayed that I tried to use a secure cookie on a non-secure connection.

    setcookie('session2', session_id(), time() + 3600, '/', "", false, false);
    setcookie('session2', session_id(), time() + 3600, '/', "", true, true);
    

    In this case I would try to identify your session_ function usage and your setcookie() usage.

    Since you are not using any particular framework I would give my first guess to autoloader and/or an include or require statement which is used twice inside your project.

    Please let me know how you load your files inside your project.

    3. (Offtopic) Your site is sending cookies for your assets

    I'm not sure if your assets really need this but I would avoid sending cookies for your assets. I would say in most cases it's not relevant to have cookie handling on your assets. I would remove them. This will reduce the amount of potential cookie conflicts in your browser and cookie issues can be reduced to the relevant requests.

    4. (Offtopic) Minimal change to your condition

    I'm not sure if HTTPS is always lowercase or not but php's $_SERVER documentation states:

    Set to a non-empty value if the script was queried through the HTTPS protocol. Note: Note that when using ISAPI with IIS, the value will be off if the request was not made through the HTTPS protocol.

    According to this I would change the condition to:

    if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
       // ssl mode..
    }