Upcoming SameSite Cookie Changes in ASP.NET and ASP.NET Core

Avatar

Barry

SameSite is a 2016 extension to HTTP cookies intended to mitigate cross site request forgery (CSRF). The original design was an opt-in feature which could be used by adding a new SameSite property to cookies. It had two values, Lax and Strict. Setting the value to Lax indicated the cookie should be sent on navigation within the same site, or through GET navigation to your site from other sites. A value of Strict limited the cookie to requests which only originated from the same site. Not setting the property at all placed no restrictions on how the cookie flowed in requests. OpenIdConnect authentication operations (e.g. login, logout), and other features that send POST requests from an external site to the site requesting the operation, can use cookies for correlation and/or CSRF protection. These operations would need to opt-out of SameSite, by not setting the property at all, to ensure these cookies will be sent during their specialized request flows.

Google is now updating the standard and implementing their proposed changes in an upcoming version of Chrome. The change adds a new SameSite value, “None”, and changes the default behavior to “Lax”. This breaks OpenIdConnect logins, and potentially other features your web site may rely on, these features will have to use cookies whose SameSite property is set to a value of “None”. However browsers which adhere to the original standard and are unaware of the new value have a different behavior to browsers which use the new standard as the SameSite standard states that if a browser sees a value for SameSite it does not understand it should treat that value as “Strict”. This means your .NET website will now have to add user agent sniffing to decide whether you send the new None value, or not send the attribute at all.

.NET will issue updates to change the behavior of its SameSite attribute behavior in .NET 4.7.2 and in .NET Core 2.1 and above to reflect Google’s introduction of a new value. The updates for the .NET Framework will be available on December 10th. .NET Core updates will be available with .NET Core 3.1 starting with preview 1, in early November. .NET 3.0 and 2.1 updates will release on 19th November.

.NET Core 3.1 will contain an updated enum definition, SameSite.Unspecified which will not set the SameSite property.

The OpenIdConnect middleware for Microsoft.Owin v4.1 and .NET Core will be updated at the same time as their .NET Framework and .NET updates, however we cannot introduce the user agent sniffing code into the framework, this must be implemented in your site code. The implementation of agent sniffing will vary according to what version of ASP.NET or ASP.NET Core you are using and the browsers you wish to support.

For ASP.NET 4.7.2 with Microsoft.Owin 4.1.0 agent sniffing can be implemented using ICookieManager;

public class SameSiteCookieManager : ICookieManager
{
  private readonly ICookieManager _innerManager;

  public SameSiteCookieManager() : this(new CookieManager())
  {
  }

  public SameSiteCookieManager(ICookieManager innerManager)
  {
    _innerManager = innerManager;
  }

  public void AppendResponseCookie(IOwinContext context, string key, string value,
                                   CookieOptions options)
  {
    CheckSameSite(context, options);
    _innerManager.AppendResponseCookie(context, key, value, options);
  }

  public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
  {
    CheckSameSite(context, options);
    _innerManager.DeleteCookie(context, key, options);
  }

  public string GetRequestCookie(IOwinContext context, string key)
  {
    return _innerManager.GetRequestCookie(context, key);
  }

  private void CheckSameSite(IOwinContext context, CookieOptions options)
  {
    if (options.SameSite == SameSiteMode.None && DisallowsSameSiteNone(context))
    {
        options.SameSite = null;
    }
  }

  public static bool DisallowsSameSiteNone(IOwinContext context)
  {
    // TODO: Use your User Agent library of choice here.
    var userAgent = context.Request.Headers["User-Agent"];
    if (string.IsNullOrEmpty(userAgent))
    {
        return false;
    }
    return userAgent.Contains("BrokenUserAgent") ||
           userAgent.Contains("BrokenUserAgent2")
  }
}

And then configure OpenIdConnect settings to use the new CookieManager;

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
    // … Your preexisting options … 
    CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});

SystemWebCookieManager will need the .NET 4.7.2 or later SameSite patch installed to work correctly.

For ASP.NET Core you should install the patches and then implement the agent sniffing code within a cookie policy. For versions prior to 3.1 replace SameSiteMode.Unspecified with (SameSiteMode)(-1).

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        // TODO: Use your User Agent library of choice here.
        if (/* UserAgent doesn’t support new behavior */)
        {
               // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1)
               options.SameSite = SameSiteMode.Unspecified;
         }
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
        options.OnAppendCookie = cookieContext => 
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext => 
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });
}

public void Configure(IApplicationBuilder app)
{
    app.UseCookiePolicy(); // Before UseAuthentication or anything else that writes cookies.
    app.UseAuthentication();
    // …
}

Under testing with the Azure Active Directory team we have found the following checks work for all the common user agents that Azure Active Directory sees that don’t understand the new value.

public static bool DisallowsSameSiteNone(string userAgent)
{
    if (string.IsNullOrEmpty(userAgent))
    {
        return false;
    }

    // Cover all iOS based browsers here. This includes:
    // - Safari on iOS 12 for iPhone, iPod Touch, iPad
    // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
    // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
    // All of which are broken by SameSite=None, because they use the iOS networking stack
    if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12"))
    {
        return true;
    }

    // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
    // - Safari on Mac OS X.
    // This does not include:
    // - Chrome on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && 
        userAgent.Contains("Version/") && userAgent.Contains("Safari"))
    {
        return true;
    }

    // Cover Chrome 50-69, because some versions are broken by SameSite=None, 
    // and none in this range require it.
    // Note: this covers some pre-Chromium Edge versions, 
    // but pre-Chromium Edge does not require SameSite=None.
    if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
    {
        return true;
    }

    return false;
}

This browser list is by no means canonical and you should validate that the common browsers and other user agents your system supports behave as expected once the update is in place.

Chrome 80 is scheduled to turn on the new behavior in February or March 2020, including a temporary mitigation added in Chrome 79 Beta. If you want to test the new behavior without the mitigation use Chromium 76. Older versions of Chromium are available for download.

If you cannot update your framework versions by the time Chrome turns the new behavior in early 2020 you may be able to change your OpenIdConnect flow to a Code flow, rather than the default implicit flow that ASP.NET and ASP.NET Core uses, but this should be viewed as a temporary measure.

We strongly encourage you to download the updated .NET Framework and .NET Core versions when they become available in November and start planning your update before Chrome’s changes are rolled out.

January 17 Update: Azure have announced their plans for the .NET Same-Site Framework update.

70 comments

Leave a comment

  • Rowan Merewood
    Rowan Merewood

    A minor correction to:
    However browsers which adhere to the original standard and are unaware of the new value have a different behavior to browsers which use the new standard as the SameSite standard states that if a browser sees a value for SameSite it does not understand it should treat that value as “Strict”.

    This is the bug in iOS and MacOS: https://bugs.webkit.org/show_bug.cgi?id=198181 It does not correspond to earlier versions of the standard.

    However, that earlier version: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02 does still state the following:
    If the “SameSite” attribute’s value is neither of these, the cookie will be ignored.

    This is the behaviour exhibited by browsers implementing the previous version of the spec and is still incompatible, just with a slightly different effect of rejecting the cookie entirely.

  • Todd Snyder
    Todd Snyder

    Checking to see if there will be an updated blog post when the fix is available? Or an ETA when Microsoft.Owin 4.1.0 will be released?

    Plus, has anyone had any luck with setting the chrome flags. With Chrome 76 we were able to set the flags and see an error coming back from OpenId/Microsoft.Owin. With the latest version (Chrome 78), Chrome Beta 79 and the canary version of Chrome 80 setting the flags no longer raises an error. We do still see the warnings in Chrome console.

    • Avatar
      Barry Dorrans

      Unfortunately circumstances outside our control have pushed back the release until January. We’re still trying to figure out if we can release 4.8 earlier but it’s not looking hopeful.

      The flags contain a temporary time based mitigation, where if a post comes within a short period of time Chrome will let it all happen. This will go away in future versions.

      • Todd Snyder
        Todd Snyder

        Thanks for the update. Do you have a better ETA on when the release will be available in January, if not at least a date when we might get a more confirmed release date?

        Are there other options that customers could pursue to work around the issue?

          • Avatar
            Robert Slaney

            Doesn’t leave a lot of time to patch codebases, get through test cycles and into production. Many orgs have deployment/code freezes over the XMas/New Year period due to reduced resources.

            Will the fix include the System.Web cookie handler, not just Owin. We had to ditch the native Owin cookie handler and forward cookie handling back to System.Web. ASP.NET was (seemingly) randomly clearing the OWIN set-cookie headers.

    • Avatar
      Barry Dorrans

      The browser sniffing code? No, we won’t.

      I don’t know if you remember browsercap.ini, but it was a file in asp.net which used to user browser agents to provide information about whether a browser supported features like javascript, frames and so on. It had no update mechanism other than a new version of the .NET Framework which shipped maybe once a year at most, so it was woefully out of date always.

      The sniffing code is very much like that. We can’t auto update it, we don’t know what browsers you care about, and we don’t want to make assumptions that may work for one customer but not for another. So we’ll provide sample code for you to use and customise, with the browser strings that Azure Active Directory have helped us with, the ones they see, but we won’t be providing this in binary form.

      • Clement Gutel
        Clement Gutel

        Thanks for the quick answer, I see what you’re saying.
        I wasn’t expecting an official Microsoft.* package but it seems like some sort of unofficial/unsupported community package would be useful rather than everyone tweaking their own sniffing logic…

        • Charles Chen
          Charles Chen

          Clement,

          Google’s guidance — and as implemented by the Auth0 team — is to simply issue two duplicate cookies.

          One of the cookies will carry the flag and one will not. This provides 100% browser compatibility coverage since the browser can then have both cookies to work with; it does not rely on browser sniffing. There are various ways that you can issue the two cookies based on your platform and there are different solutions for different versions and flavors of .NET.

          However, there is a super simple solution that will work for ALL versions of .NET and ALL production supported versions of IIS and ALL flavors of .NET: use an IHttpModule to duplicate the outbound cookie and add the flag to the duplicate and then on the inbound response, coalesce the duplicate cookie if necessary.

          It should take no more than 15-20 lines of code and is entirely side loaded. This means you can apply the solution whether you own the original source or not, whether the code is .NET Core 1, .NET Core 2, .NET Core 3, .NET Framework 2, .NET Framework 4, and so on.

          Any team can get this solution in place in under an hour.

          The heart of the fix is here:

          using System;
          using System.Linq;
          using System.Web;
          
          namespace SameSiteHttpModule
          {
              public class SameSiteDoomsdayModule : IHttpModule
              {
                  /// <summary>
                  ///     Set up the event handlers.
                  /// </summary>
                  public void Init(HttpApplication context)
                  {
                      // This one is the OUTBOUND side; we add the extra cookie
                      context.PreSendRequestHeaders += OnEndRequest;
          
                      // This one is the INBOUND side; we coalesce the cookies.
                      context.BeginRequest += OnBeginRequest;
                  }
          
                  /// <summary>
                  ///     The OUTBOUND LEG; we add the extra cookie.
                  /// </summary>
                  private void OnEndRequest(object sender, EventArgs e)
                  {
                      HttpApplication application = (HttpApplication)sender;
          
                      HttpContext context = application.Context;
          
                      // IF NEEDED: Add URL filter here
          
                      for (int i = 0; i < context.Response.Cookies.Count; i++)
                      {
                          HttpCookie responseCookie = context.Response.Cookies[i];
          
                          context.Response.Headers.Add("Set-Cookie", $"{responseCookie.Name}-same-site={responseCookie.Value};SameSite=None; Secure");
                      }
                  }
          
                  /// <summary>
                  ///     The INBOUND LEG; we coalesce the cookies.
                  /// </summary>
                  private void OnBeginRequest(object sender, EventArgs e)
                  {
                      HttpApplication application = (HttpApplication)sender;
          
                      HttpContext context = application.Context;
          
                      // IF NEEDED: Add URL filter here
          
                      string[] keys = context.Request.Cookies.AllKeys;
          
                      for (int i = 0; i < context.Request.Cookies.Count; i++)
                      {
                          HttpCookie inboundCookie = context.Request.Cookies[i];
          
                          if (!inboundCookie.Name.Contains("-same-site"))
                          {
                              continue; // Not interested in this cookie.
                          }
          
                          // Check to see if we have a root cookie without the -same-site
                          string actualName = inboundCookie.Name.Replace("-same-site", string.Empty);
          
                          if (keys.Contains(actualName))
                          {
                              continue; // We have the actual key, so we are OK; just continue.
                          }
          
                          // We don't have the actual name, so we need to inject it as if it were the original
                          // https://support.microsoft.com/en-us/help/2666571/cookies-added-by-a-managed-httpmodule-are-not-available-to-native-ihtt
                          context.Request.Headers.Add("Cookie", $"{actualName}={inboundCookie.Value}");
                      }
                  }
          
                  public void Dispose()
                  {
                      
                  }
              }
          }

          If you are interested in more details, you can see here: https://charliedigital.com/2020/01/22/adventures-in-single-sign-on-samesite-doomsday/

          • Avatar
            Barry Dorrans

            The reason we didn’t take this approach is that once authentication cookies are in the mix it becomes incredibly easy to hit the cookie limits, particularly in iOS browsers which have a very small limit on cookie sizes per domain.

          • Charles Chen
            Charles Chen

            Barry,

            I think it’s important that if you provide that reason, you also cite the data so that folks on those limits so folks can make the right decision given the short timeline now. There is a resource here: http://browsercookielimits.squawky.net/ but it’s very much outdated and incomplete; hopefully it can be of use for folks dealing with iOS.

            Risk/reward wise, I’d advise folks to look at their server logs and determine how much risk there is. I don’t know anyone who uses iOS who do not also have Firefox and/or Chrome installed.

            Best of luck to everyone that has to get this wrapped up in the next few weeks!

          • Simo Bougtaibi
            Simo Bougtaibi

            Hi,
            We are in the same situation of IOS limit of cookies size, cookies are not sent to server and user lost authentification , witch approach is best to resolve it ?
            We have some cookies that we cannot touch , because of partenership owners , our website is still on asp.net MVC not on .net core .. Can you provide some way to keep site working on both browsers and with managing same site : HttpModule ? handlers ? Patch .net Framework ? help !
            Best regards

  • Clement Gutel
    Clement Gutel

    As an alternative solution, how about issuing 2 cookies?
    One with SameSiteMode.Unspecified and one with SameSiteMode.None.
    At least one of the two cookies should be sent, regardless of the standard version implement by the browser.
    Obviously, the code would need to change to be aware of the 2 cookies, but at least it doesn’t rely on user agent sniffing.

    • Avatar
      Barry Dorrans

      We thought about that, but that would mean everyone having to wait on every component to get updated. With the approach we choose it doesn’t matter if your third party component is unaware of the changes, the central cookie handling will take over and do the right thing.

        • Avatar
          Barry Dorrans

          And that’s a single scenario. Which, depending on the number of claims in your auth cookie, may break under iOS due to stricter limitations on the size of cookies a single domain can issue. Which is then doubled by issuing double cookies.

          By using the single extension point we can support multiple scenarios, multiple libraries which write cookies and which have not, or cannot be updated.

          • Clement Gutel
            Clement Gutel

            Got it, but if we implement the double cookie ourselves (I really don’t want to sniff the agent), this should work fine, right?
            (We have a low number of claims and our cookies are pretty small so size is unlikely to be a problem for us…)

      • Avatar
        Dominik Rauch

        @Barry Dorrans: According to the experts at Auth0 (https://auth0.com/blog/browser-behavior-changes-what-developers-need-to-know/) you should definitely go for the recommended Google workaround as browser sniffing does not work in all scenarios.

        See especially:

        We could use UA Sniffing to detect Chrome 80+ and only apply SameSite=None for that, right? Again, not as simple. Due to the WebKit bug forever present on iOS 12, you’d also need to factor in the operating system major version and the rendering engine. Plus, other browser vendors are already signaling that they’ll be adopting these changes. In time, when this becomes an official standard, all browsers will. We’re seeking a solution with few drawbacks that works on all browsers, regardless of their age, vendor, operating system, or rendering engine.

        So please go for the multi-cookie approach!

        Best regards,
        D.R.

          • Avatar
            Dominik Rauch

            So the only problem is mobile browsers? => What problems exactly?

            For us this is the best approach

            If you’re reading the Auth0 article, you’ll recognize that browser sniffing is simply not going to work. Don’t know why you opted for this “best approach”. Is there a GitHub issue with more background information about your decision process and all the pros/cons? If I read the Auth0 article correctly, nobody can use your proposed solution without running into problems.

            Best regards,
            D.R.

          • Avatar
            Barry Dorrans

            For some reason wordpress isn’t letting me reply to your newer comment.

            There is no github issue, this approach spans more than the open source .NET Core, and the decision was made taking into account the concerns of multiple products internally and the multiple browser scenarios they have to decide. Auth0’s requirements are simpler, they just have to support Auth0, and customers than can update their single library. .NET cannot limit themselves to that scenario.

          • Avatar
            Dominik Rauch

            (I can’t reply to your last comment either…)

            OK, if you have an application which does not run into max cookie count problems then the double cookie policy is probably the best solution. Unfortunate, that ASP.NET Core doesn’t allow you to choose but recommends a single “best for everyone” solution. But I can see that it is too late now to change this decision.

            I’ve created a GitHub issue on how to implement the double cookie policy ourselves.

            Best regards,
            D.R.

          • Avatar
            Robert Slaney

            Taking into account the chunking cookie manager to get around individual set-cookie header limits, this becomes an absolute nightmare is we have to double up every cookie

            The list of strict samesite 2016 spec browsers is small and doesn’t force double cookies on the other 99% of browsers

        • Charles Chen
          Charles Chen

          Dominik,

          I implemented a variant of Auth0’s multi-cookie approach using an IHttpModule:

          https://charliedigital.com/2020/01/22/adventures-in-single-sign-on-samesite-doomsday/

          This solution can be “side-loaded” and the general approach of using a proxy to manage the issuance and coalescing of the duplicate cookies can be applied to any server architecture.

          Wanted to share this for any folks who are facing an imminent mitigation for this change in the next few weeks!

  • Chris DaMour
    Chris DaMour

    with this change the behaviour went in .net core from None meaning DO NOT set samesite param to set it as samesite=none.

    https://github.com/aspnet/AspNetCore/pull/13858/files

    this made the suggested fixes for https://github.com/aspnet/AspNetCore/issues/4647 completely uneffective. This is the iOS 12/safari 12 bug where it interprets samesite=none as strict. That’s quite a backwards incompatible change.

    I think this was a mistake as we’re breaking behaviour within a patch version. Why wasn’t this done as an opt in? at the very least the docs for asp.net core no longer are correct

    https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.samesitemode?view=aspnetcore-2.1 -> “None 0 No SameSite field will be set, the client should follow its default cookie policy.”

    ditto https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.samesitemode?view=aspnetcore-2.2 (and all the way to 3.0 the docs for 3.1 seem to be missing).

    This is contrast to asp.net https://docs.microsoft.com/en-us/dotnet/api/system.web.samesitemode?view=netframework-4.7.2 that makes mentions of the the behaviour changing.

    This really sucks because all of a sudden my app is now adding samesite=none to openidconnect cookies when i explicitly did not want them because of iOS 12 users who can’t work with em.

    Note to whoever if you want to opt out of this change for now https://docs.microsoft.com/en-us/dotnet/core/compatibility/aspnetcore#opt-out-switches

    • Avatar
      Barry Dorrans

      Unfortunately we’re damned if we do and damned if we don’t, hence the documented browser sniffing approach.

      If we don’t update we lock out Chrome, if we do you have to do some work to support older browsers. We took the path that supports the more popular browsers by default. While you can opt-out for now, or not install the update on your servers (its not a security update) you will have to do something before Chrome implements its incompatible change.

  • Avatar
    Jaap Mosselman

    I have .NET Framework 4.7 ASP.NET MVC application using Owin with Owin Ws-Federation for AAD authentication. After the .NET Framework update was installed on the server last week, we could not authenticate with AAD in some cases. I updated to Owin 4.1 but that did not fix the problem.
    When I used Chrome (79) it dit not work, but when I use Edge it just works.
    So I analyzed by debugging the application (got Owin code locally to debug it). What I saw was that the /signing-federation POST callback is just there with the correct information. The Owin Ws-Federation middleware sets the .AspNet.External cookie with the identity. But for some reason that cookie seems to be left out from the actual written response. With Chrome it is not in the response. With Edge it is. I looked even with Fiddler to the response to verify that. I can’t see why it would be UA dependent. So it seems randomly. In my opinion this has nothing to do with the SameSite changes which just should do something with a value inside the cookie.
    I noticed also that Robert Slaney in a previous comment mentioned something about Owin Cookies randomly removed. No idea if this is the same problem.
    Any idea what’s going on here?

  • Avatar
    Rune Moberg

    I tried this in my web.config:

    <httpCookies sameSite=”none” requireSSL=”true” />

    But it gets ignored. IIS still sets this cookie:

    set-cookie: ASP.NET_SessionId=a5hrhikf5wvykxw3gxeqtph1; path=/; secure; HttpOnly; SameSite=Lax

    as Lax. Which causes problems down the road when I get a POST back from the site taking care of SSO for us.

    Edit: Never mind! Found the correct invocation:

    <sessionState cookieSameSite=”None” />

  • Robert Slaney
    Robert Slaney

    It looks like the AntiForgeryToken system in MVC 5 ( via Microsoft.AspNet.WebPages ) hasn’t been updated for samesite. It’s still writing directly to ASP.NET’s response cookies without any samesite value.

    Should the AntiForgeryConfig static class be updated ?

  • Avatar
    Martin Brown

    We’re having trouble with deploying this with an Azure Web App.

    The site being deployed targets .Net 4.7.2, and the changes work when tested locally as expected.

    If we decompile the System.Web.dll (downloaded through Kudu) we’re seeing an older version that doesn’t handle samesite cookies.

    This appears to be an issue for others (with 4.7.2 despite the 4.8 topic).

    https://feedback.azure.com/forums/169385-web-apps/suggestions/37566262-upgrade-app-service-with-net-4-8

    Would the expectation be that Azure would have also been patched now? The timestamp on the System.Web.dll is 11/12/2019 but decompiled seeing:

                if (this._sameSite != SameSiteMode.None)
                {
                    stringBuilder.Append("; SameSite=");
                    stringBuilder.Append(this._sameSite);
                }
  • Jose Rivas
    Jose Rivas

    We Have an app running on 4.8. implemented the same-site fix. it works locally but when deployed to the server the user-agent is not there. so it fails.
    any idea as to why? local is iisExpress and server is windows server 2016 with iis10. server also has 4.8 runtime. all owin packages are updated to 4.1..

  • Avatar
    Ronald Bosma

    We have IdentityServer4 with .NET Core 2.1 and followed the instructions. Updated all packages, SDK, runtime, etc. to the latest versions and made the code changes. Still didn’t get SameSite=None on our IdentityServer cookies.

    In the end I found this issue: https://github.com/dotnet/aspnetcore/issues/18265. The Microsoft.Net.Http.Headers assembly was still on 2.1.1. We added a direct reference to 2.1.14 in our .csproj. This fixed the problem.

  • Avatar
    Adam Freeman

    We also ran into this issue today on our staging site as it seems that Chrome 79 started causing trouble. To fix it we added cookieSameSite=”None” to the forms tag nested under the authentication tag in system.web

  • Avatar
    Kendall Bennett

    This is an absolute disaster and I don’t think Google nor Microsoft has yet realized the complete shit storm they just unleashed. Over the weekend I decided I would patch our web servers with the latest updates, only to find those .net updates now decided to start setting SameSite=lax by default, where before nothing was set at all. In all their wisdom the morons at Google never realized the impact the SameSite=lax has on REAL FUCKING BUSINESS CODE. Guess what the impact of using SameSite=lax does when it is applied to your session cookie? It means you session is lost if you POST from another site back to your web site. Guess what the most common usage is for POST’ing data back from another web site to your web site is? POST backs from payment providers, like Affirm, 3D Secure etc.

    So now with this change rolled out, suddenly we have customers unable to check out on our web site! Oh what fun.

    Oh but you say, you should not be doing a POST. You should be doing a GET, and then you do get the cookie back when SameSite=lax. Oh really? You do realize when you do a GET, all the parameters END UP IN YOURS FUCKING LOGS! So yeah, no. You DO NOT EVER SEND BACK SENSITIVE DATA VIA GET.

    Took an entire day to figure out it was NOT a code change that we made at all, but rather that I patched the servers. We figured this out because I had left one server unpatched, because that server runs all our shipping tools and is our image server, but it does not have any redundancy via a load balancer (the shipping tools do not support that). None of that stuff is super critical if it goes down, and we have emergency plans to quickly restore it if the machine goes tits up. So I left it unpatched and we would tackle that one during the normal out of hours update process when we can afford for it to be down for a few minutes for a reboot (yeah, how much do I wish Windows could finally apply updates without rebooting like Linux can!). So long story short, after reproducing the checkout problems with Affirm I was able to discover that running directly against that other server, it worked fine.

    So we finally ended up working out what the problem was, found this blog article and ended up making the necessary changes to set SameSite=none, and our customers were now able to checkout with Affirm or with banks that require 3D Secure (like a lot of them). Many sites using older versions of PayPal will also be running into this problem, but thankfully for us we got rid of POST backs with PayPal a long time ago so it was not affected.

    So we are all good now, right? Ignoring the fact that for sites using software that is not easily updated, they are pretty much fucked when Google rolls this out permanently in Chrome in a few weeks, there are some more glaring problems. As mentioned here, some clients don’t do the right thing when SameSite=none:

    https://www.chromium.org/updates/same-site/incompatible-clients

    Oh, and guess what clients those are? Shitty, older versions of Chrome (well actually not that old and shitty, but at least Chrome force updates itself), and, wait for it, ALL VERSIONS OF SAFARI ON macOS 10.14 (Mojave) and iOS 12. Oh what? Hasn’t Apple patched that? Well yes they have. Pretty quickly actually, but for some stupid reason Apple has not pushed those patches to anything other than macOS 10.15 (Catalina) and iOS 13. We tested it with the latest version of iOS 12, and it does not work.

    So guess what I walked into this morning. Frantic customers calling saying they cannot log into our web site. Ugh!

    So now the solution we are implementing today is we need to fingerprint the browser so we can detect those older, shitty versions of Safari (and Chrome) and completely remove the SameSite cookie parameter as none of the parameter values will work at all on those browsers. I sure hope I can actually find a way within .net to actually REMOVE the cookie value rather than set it to value that won’t work.

    But the long shot of this is that Chrome is about to make SameSite=lax the default for all sites that do not currently set it to something. So all those sysadmins who are way behind on patches and have not actually updated their servers in a while and are currently immune to this problem, will suddenly have issues and probably have zero idea how to solve it. Some of them may not even have the ability to fix it and could potentially go out of business. All because some jackasses who think they are security experts decided that all this was a good ideas, because, well, they are experts.

    As soon as Chrome makes these default changes, large parts of the internet will break. I hope at that time Firefox suddenly takes over and puts Chrome in it’s place. And I hope Google gets chastised at large for this kind of crap.

    And guess what is coming next? Removing the ability to fingerprint the browser using the user agent string. Soon enough Chrome will just present a common face to everyone and we won’t know anymore what browser the user is connecting with. So next time Google, Microsoft, Apple or someone else who has decided to anonymize the browser string makes a breaking change, some web sites are going to be fucked and broken for good with no way to fix it. Like literally the fix I am about to implement today would be impossible if this change to the user agent string had been rolled out 2 years ago.

    • Avatar
      James B

      This is more Microsoft’s fault than Google’s, in my estimation. It looks like the November 2019 security update added SameSite=Lax to every dang cookie, with the promise that you can change the behavior with edits to your web.config. Only actually, if your application is older than .NET 4.7.2, you can’t, because all the various flavors of sameSite attributes are unsupported, as far as I can tell. I ended up having to install the Rewrite module and add a rule to rewrite my outgoing SetCookie headers manually and change them to SameSite=None.

  • Avatar
    Siladitya Basu

    We migrated from .net 4.52 to .net 4.7.2 version, our web server (IIS 8.5) is on Windows 2012 R2. We made the changes in the web.config as below to set cookieSameSite as “None”, but dont see SameSite as “None” in developer tool when we check the cookie. Does it require Windows Server 2019 and latest security
    patch (2019-12 Cumulative Update for .NET Framework 3.5 and 4.7.2 for Windows 10 Version 1809 for x64 (KB4533013))? This security is not available for Windows 2012 R2 ?

  • Aaron Sherman
    Aaron Sherman

    I have tried the above code on asp.net 4.72 and I never see the cookie for .AspNet.ExternalCookie (the core part of how owin passes authentication to the next page) being set. I see other cookies going through the SameSiteCookieManager from both CookieAuthenticationOptions and OpenIdConnectAuthenticationOptions. It is as if that cookie is lost in a black hole of being set.

    To the best of my knowledge I have updated
    * owin
    * .net framework

    I have tested on a few of my different computers on vs 2017 and vs 2019.

    If we use the “roll back” technique of aspnet:SuppressSameSiteNone I am able to see the cookie written in the browser, but not anywhere I can get a breakpoint.

    Anyone else seeing this?

    Thanks, Aaron

  • Avatar
    Linus Ekström

    I’ve been tracking an issue with preview of PDF files in Google Chrome for Asp.Net based sites with Forms Authentication that seems to be the cause of the recent .Net security patch. The issue appears when the PDF is big enough to cause chunked download when previewing the PDF. Google will treat the chunked requests as cross-site requests – and thus drop any cookie with samesite set to LAX. In my opinion – this is an issue with how Chrome handles chunked requests. More information is available on my blog post: https://www.epinova.se/en/blog/issues-with-pdf-preview-for-secured-pdfs-in-google-chrome-due-to-.net-security-patch/