sameSite Automated Fix and status reporting tool

Last updated on February 12, 2020

It has been a hard week with all these vendors announcing the Four Hoursemen of the Cookies Apocalypse arrival.

There’re a lot of changes coming when we talk about cookies ( 1st, 3rd party ), ITP, GDPR, CCPA,etc . I understand it may be a terrible headache for anyone but we need to keep some calm.

Last update has came from Google Chrome, which was expected to start blocking the cookies not containing the sameSite attribute on 4th February. Luckily they have postponed around 2 weeks ( for now ).

One of the main concerns about this latests Chrome update 80 is that it’s not up to us to fix things ( or it is? ). If a cookie is being set via JS, that JS owner is the one in charge for setting the cookie properly. So we may expect some old libraries not being updated on time, or some vendors not even caring about taking care of this properly.

In order to deal with the sameSite switch for cookies, I’m releasing today a JS snippet that will hook in the document.cookie execution flow and will take care of two main tasks:

  • Reporting all the cookies that are not setting the sameSite value properly ( remember they need to have the sameSite and the Secure switches )
  • If we decide to turn it on, the script will be able to automatically add sameSite and Secure parameters automatically!

The script I’m sharing will take care reporting on the window.load event a list of cookies that didn’t either set the sameSite or the Secure switches for the cookies, and will also report the cookie name, if the cookie setting has been autofixed and the original and fixed cookie setting string.

'event': 'samesite-cookies-report',
'cookiesList': [{
                            'cookieName': {{cookie.name}},
                            'cookieSameSite': {{cookie.secure}},
                            'cookieSecure': {{cookie.sameSite}},
                            'autofixed': {{autoFix}},
                            'originalCookieString': {{cookie.string}},
                            'fixedCookieString': {{cookie.fixedString}}
}]

Then based on the samesite-cookies-report event on Google Tag Manager you could push this details as an event to Google Analytics or report it to anywhere else.

Main point of this script is being able to monitorize any cookie being set somewhere in our website, so we can contact their stakeholder to have it fixed as soon as posible

<script>
(function() {
  try {
    // Set this to true if you want to automatically add the sameSite attribute        
    var autoFix = false;

    var cookiesReportList = [];

    // Detecting if the current browser is a Chrome >=80
    var browserDetails = navigator.userAgent.match(/(MSIE|(?!Gecko.+)Firefox|(?!AppleWebKit.+Chrome.+)Safari|(?!AppleWebKit.+)Chrome|AppleWebKit(?!.+Chrome|.+Safari)|Gecko(?!.+Firefox))(?: |\/)([\d\.apre]+)/);
    var browserName = browserDetails[1];
    var browserVersion = browserDetails[2];
    var browserMajorVersion = parseInt(browserDetails[2].split('.')[0]);

    // We only want to hook the cookie behavior if it's Chrome +80 
    if (browserName === 'Chrome' && browserMajorVersion >= 80) {
      var cookie_setter = document.__lookupSetter__('cookie');
      var cookie_getter = document.__lookupGetter__('cookie');

      Object.defineProperty(document, "cookie", {
        get: function() {
          return cookie_getter.apply(this, arguments);
        },
        set: function(val) {
          var cookie = {
            name: '',
            sameSite: false,
            secure: false,
            parts: val.split(';'),
            string: val
          }

          cookie.parts.forEach(function(e, i) {
            var key = e.trim();
            cookie.parts[i] = e.trim();
            if (i === 0) {
              cookie.name = key.split('=')[0];
            }

            if (key.match(/samesite/)) {
              cookie.sameSite = true;
            }

            if (key.match(/secure/)) {
              cookie.secure = true;
            }
          });

          if (cookie.sameSite === false || cookie.secure === false) {
            if (autoFix === true && document.location.protocol==="https:") {
              if (arguments[0][arguments[0].length - 1]) {
                arguments[0] = arguments[0].substring(0, arguments[0].length - 1);
              }
              if (cookie.sameSite === false) {
                arguments[0] = arguments[0] + '; sameSite=None';
              }
              if (cookie.secure === false) {
                arguments[0] = arguments[0] + '; secure';
              }
            }
            cookiesReportList.push({
              'cookieName': cookie.name,
              'cookieSameSite': cookie.sameSite,
              'cookieSecure': cookie.secure,
              'autofixed': autoFix,
              'originalCookieString': cookie.string,
              'fixedCookieString': arguments[0]
            });
          }
          return cookie_setter.apply(this, arguments);
        }
      });
    }
    window.addEventListener('load', function(event) {
      dataLayer.push({
        'event': 'samesite-cookies-report',
        'cookiesList': cookiesReportList
      });
    });
  } catch (err) {}
})();
</script>

On the top of code you may see the following line:

var autoFix = false;

ok, if we change this to true, the script will automatically take care of accordingly adding the missing parts 🙂

One of the things to have in mind is that we need this code to be run as soon as possible on the page execution flow, so if we’re setting this via GTM, we’ll need to setup this tag to fire on the first event on the page ( most of time will be “All Pages ), and give it some extra priority:

WARNING: If you only plan to use this script as a reporting tool you can stay safe. If you plan to use the autofixing feature, please have in mind that I only tested in some sites of mine, so it’s your liability to properly setting and testing it up in your site before going live.

If you’re really interested on knowing more about how cookies are updating their behaviour to protect the users privacy, best place is https://www.cookiestatus.com/ . A site where my friend Simo is collecting all the info about almost all the mainstream browsers out there.

4 Comments

  1. February 10, 2020
    Reply

    Hi David,

    Thanks for sharing the script. I must admit I have not tried it yet but I spotted something in your code. I am not sure that’s an error but it looked strange:

    ‘cookieSameSite’: cookie.secure,
    ‘cookieSecure’: cookie.sameSite,

    Should not that read the following instead?:

    ‘cookieSameSite’: cookie.sameSite,
    ‘cookieSecure’: cookie.secure,

    Thanks,

    Alban

    • February 10, 2020
      Reply

      Well spotted, last minute refactors are evil. Thanks for reporting it, already fixed.

  2. February 12, 2020
    Reply

    Wow this is elegant! Didn’t realize you could “hijack” document.cookie this way. Hacky, absolutely, but it’s a plug-n-play near-universal stopgap solution, and seems to be the best (and in some cases, only) option for third-party cookies set by clientside JS. Thank you so much for sharing this!

    Had a thought re limiting this to Chrome 80+. There’s clear reason to do so, but I’m curious if there’s other thinking behind that decision. Since other browsers will be adopting the same standards, and in the meantime allow or disregard SameSIte=None, I wonder if it’s better to exclude incompatible browsers rather than only include Chrome 80+. Thoughts?

    One other callout: with autoFix on, this script disallows any cookies from being set on non-HTTPS pages. Maybe disable autoFix completely when protocol=http?

    • February 12, 2020
      Reply

      Hi Stephen, this script was initially thought to monitor the cookies not being set the sameSite switch, and before publishing it I decided to add the autofix switch, which this i mean it was really designed to “fix” an issue, more rather to help on properly identifying the source and in a last instance being able to fix if any third party was not collaborating. With this I mean if your site has any vendor not fixing their stuff properly you could use this, but not a long-term solution ( imho ).

      Limiting it to Chrome 80+ is because is the only one one being affected right now, I don’t see any real reason to run this code that can be considered too instrusive for no reason, at some point it can be extended with no difficulties, even the browser identifying regex, covers the rest of browsers (most of them).

      And yep, I’m adding that condition, I didn’t work on any site that wasn’t https in the last 18 months so I was almost assumed it. Thanks for this.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.