Search code examples
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?

  • Gecko Bug 1617361 is when Gecko randomly chooses to not set the HTTP Cookie header.
  • Gecko Bug 1623284 (below) is about Gecko not bothering to mention that the session cookie was not acceptable because it's not secure and the current one is secure.
  • This bug is about figuring out how my PHP code could generate the session cookie without the security/domain/whatever flags.

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.

  • I am happy to update the question and provide any other relevant code.
  • The domain in question is linked in my profile though as I mentioned I've set the session cookie to nonsecure for now.
  • This bug seems to occur in both local/WAMP and live/LAMP environments.

Verification Requirements for Acceptable Answer

  • I need to be able to reproduce the occurrence of the session cookie when it is refusing to be sent as secure (the domain in my profile is a good example of my platform's HTTP behavior).
  • I need to understand what about my code, the environment and/or any bugs in PHP itself that are causing this issue.
  • I need to be able to reliably know that session cookies being sent in all instances are secure, especially since Gecko browsers do not tell you about the error in the developer console (posted bug 1623284 for that).
  • I do not use frameworks or libraries for any of my client/server code.

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