Trabajo con cookies de SameSite en ASP.NET Core

Por Rick Anderson

SameSite es un estándar de borrador de IETF diseñado para proporcionar cierta protección contra ataques de falsificación de solicitud entre sitios (CSRF). Redactado originalmente en 2016, el borrador del estándar se actualizó en 2019. El estándar actualizado no es compatible con el estándar anterior, siendo las diferencias más notables:

  • Las Cookies sin encabezado de SameSite se tratan como SameSite=Lax de manera predeterminada.
  • SameSite=None se debe usar para permitir el uso entre sitios cookie .
  • Las Cookies que afirman SameSite=None también deben marcarse como Secure.
  • Las aplicaciones que usan <iframe> pueden experimentar problemas con sameSite=Lax o sameSite=Strictcookies porque <iframe> se trata como escenario entre sitios.
  • El valor SameSite=None no está permitido por el estándar de 2016 y hace que algunas implementaciones traten esas cookies como SameSite=Strict. Consulte Compatibilidad con exploradores más antiguos en este documento.

La configuración de SameSite=Lax funciona para la mayoría de las cookies de aplicaciones. Algunas formas de autenticación como OpenID Connect (OIDC) y WS-Federation usan de manera predeterminada redireccionamientos basados en POST. Las redirecciones basadas en POST desencadenan las protecciones del explorador de SameSite, por lo que SameSite está deshabilitado para estos componentes. La mayoría de los inicios de sesión de OAuth no se ven afectados debido a las diferencias en la forma en que fluye la solicitud.

Cada componente de ASP.NET Core que emite cookies debe decidir si SameSite es adecuado.

SameSite y Identity

ASP.NET Core Identity no se ve afectado en gran medida por cookies de SameSite excepto en escenarios avanzados como la integración de IFrames o OpenIdConnect.

Al usar Identity, no agregue ningún proveedor de cookie o llame a services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme), Identity se encarga de ello.

Código de ejemplo de prueba de SameSite

El ejemplo siguiente se puede descargar y probar:

Muestra Documento
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 3.1 cookie

Compatibilidad de .NET Core con el atributo sameSite

.NET Core admite el estándar de borrador de 2019 para SameSite. Los desarrolladores pueden controlar mediante programación el valor del mismo atributoSite mediante la propiedad HttpCookie.SameSite. Si establece la propiedad SameSite en Strict, Lax o None, esos valores se escribirán en la red con el cookie. Establecerlo en SameSiteMode.Unspecified indica que no debe enviarse ningñun sameSite con el cookie.

    var cookieOptions = new CookieOptions
    {
        // Set the secure flag, which Chrome's changes will require for SameSite none.
        // Note this will also require you to be running on HTTPS.
        Secure = true,

        // Set the cookie to HTTP only which is good practice unless you really do need
        // to access it client side in scripts.
        HttpOnly = true,

        // Add the SameSite attribute, this will emit the attribute with a value of none.
        SameSite = SameSiteMode.None

        // The client should follow its default cookie policy.
        // SameSite = SameSiteMode.Unspecified
    };

    // Add the cookie to the response cookie collection
    Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
}

Uso de API con SameSite

HttpContext.Response.Cookies.Append tiene Unspecified como valor predeterminado, lo que significa que no se ha agregado ningún atributo SameSite al cookie y el cliente usará su comportamiento predeterminado (Lax para los nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor de SameSite cookie a SameSiteMode.Lax:

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

Todos los componentes de ASP.NET Core que emiten cookies sustituyen los valores predeterminados anteriores con la configuración adecuada para sus escenarios. Los valores predeterminados anteriores invalidados no han cambiado.

Componente cookie Valor predeterminado
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Autenticación de Cookie CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 y versiones posteriores proporciona la siguiente compatibilidad con SameSite:

  • Vuelve a definir el comportamiento de SameSiteMode.None para emitir SameSite=None
  • Agrega un nuevo valor SameSiteMode.Unspecified para omitir el atributo SameSite.
  • Todas las API de cookies tienen como valor predeterminado Unspecified. Algunos componentes que usan cookies establecen valores más específicos para sus escenarios. Consulte la tabla anterior para obtener ejemplos.

En ASP.NET Core 3.0 y versiones posteriores se cambiaron los valores predeterminados de SameSite para evitar conflictos con valores predeterminados de cliente incoherentes. Las siguientes API han cambiado el valor predeterminado de SameSiteMode.Lax a -1 para evitar la emisión de un atributo SameSite para estas cookies:

Historial y cambios

La compatibilidad con SameSite se implementó por primera vez en ASP.NET Core en la versión 2.0 con el estándar de borrador de 2016. El estándar de 2016 era opcional. ASP.NET Core optó por participar estableciendo varias cookies en Lax de manera predeterminada. Después de encontrar varios problemas con la autenticación, se deshabilitó la mayoría del uso de SameSite.

Las revisiones se emitieron en noviembre de 2019 para actualizar del estándar de 2016 al estándar de 2019. El borrador de 2019 de la especificación SameSite:

  • No es compatible con el borrador de 2016. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.
  • Especifica que las cookies se tratan como SameSite=Lax de manera predeterminada.
  • Especifica que las cookies que afirman explícitamente SameSite=None para habilitar la entrega entre sitios deben estar marcadas como Secure. None es una nueva entrada para no participar.
  • Es compatible con las revisiones emitidas para ASP.NET Core 2.1, 2.2 y 3.0. ASP.NET Core 3.1 y versiones posteriores tiene compatibilidad adicional con SameSite.
  • Está previsto que Chrome lo habilite de manera predeterminada en febrero de 2020. Los exploradores empezaron a pasar a este estándar en 2019.

API afectadas por el cambio del estándar de borrador de SameSite de 2016 al estándar de borrador de 2019

Compatibilidad con exploradores más antiguos

El estándar de SameSite de 2016 obligaba a tratar los valores desconocidos como valores de SameSite=Strict. Las aplicaciones a las que se accede desde exploradores antiguos que admiten el estándar de SameSite de 2016 pueden interrumpirse cuando obtienen una propiedad de SameSite con un valor de None. Las aplicaciones web deben implementar la detección de navegadores si pretenden ser compatibles con navegadores antiguos. ASP.NET Core no implementa la detección del explorador porque los valores de User-Agents son muy volátiles y cambian con frecuencia. Un punto de extensión en Microsoft.AspNetCore.CookiePolicy permite conectar la lógica específica de User-Agent.

En Program.cs, agregue código que llame a UseCookiePolicy antes de llamar a UseAuthentication o a cualquier método que escriba cookies:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.OnAppendCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

    builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseCookiePolicy();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

En Program.cs, agregue código similar al código resaltado siguiente:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.OnAppendCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

    builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseCookiePolicy();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

En el ejemplo anterior, MyUserAgentDetectionLib.DisallowsSameSiteNone es una biblioteca proporcionada por el usuario que detecta si el agente de usuario no admite None de SameSite:

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

El siguiente código muestra un método DisallowsSameSiteNone de ejemplo:

Advertencia

El código siguiente es solo para demostración:

  • No debería considerarse completo.
  • No se mantiene ni tiene soporte.
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(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;
}

Probar aplicaciones para problemas de SameSite

Las aplicaciones que interactúan con sitios remotos, por ejemplo mediante inicio de sesión de terceros, necesitan:

Probar las aplicaciones web usando una versión cliente que pueda incorporarse al nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevas marcas de características de participación que se pueden usar para las pruebas. Una vez que la aplicación aplique las revisiones de SameSite, pruébela con versiones de cliente anteriores, especialmente Safari. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.

Prueba con Chrome

Chrome 78+ proporciona resultados engañosos porque tiene una mitigación temporal en su lugar. La mitigación temporal de Chrome 78+ permite cookies de menos de dos minutos de antigüedad. Chrome 76 o 77 con las marcas de prueba adecuadas habilitadas proporciona resultados más precisos. Para probar el nuevo comportamiento, cambie chrome://flags/#same-site-by-default-cookies a Habilitado. Se ha informado de que las versiones más antiguas de Chrome (75 e inferiores) fallan con la nueva configuración de None. Consulte Compatibilidad con exploradores más antiguos en este documento.

Google no hace que las versiones de Chrome anteriores estén disponibles. Siga las instrucciones que aparecen en Descargar Chromium para probar versiones anteriores de Chrome. No descargue Chrome desde los vínculos proporcionados mediante la búsqueda de versiones anteriores de Chrome.

A partir de la versión de Canary 80.0.3975.0, la mitigación temporal Lax+POST se puede deshabilitar con fines de prueba con la nueva marca --enable-features=SameSiteDefaultChecksMethodRigorously para permitir la prueba de sitios y servicios en el estado final de la característica en la que se ha quitado la mitigación. Para más información, consulte las Actualizaciones de SameSite de The Chromium Projects

Prueba con Safari

Safari 12 aplica estrictamente el proyecto anterior y falla cuando el nuevo valor None está en un cookie. None se evita a través del código de detección del explorador Compatibilidad con exploradores más antiguos en este documento. Pruebe los inicios de sesión de estilo del sistema operativo basados en Safari 12, Safari 13 y WebKit mediante MSAL, ADAL o cualquier biblioteca que esté usando. El problema depende de la versión del sistema operativo subyacente. Se sabe que OSX Mojave (10.14) e iOS 12 tienen problemas de compatibilidad con el nuevo comportamiento de SameSite. Actualizar el sistema operativo a OSX Catalina (10.15) o iOS 13 soluciona el problema. Safari no tiene actualmente una marca de participación para probar el comportamiento de la nueva especificación.

Prueba con Firefox

La compatibilidad de Firefox con el nuevo estándar puede probarse en la versión 68+ accediendo a la página about:config con la marca de características network.cookie.sameSite.laxByDefault. No ha habido informes de problemas de compatibilidad con versiones anteriores de Firefox.

Prueba con el explorador Edge

Edge admite el antiguo estándar de SameSite. La versión 44 de Edge no tiene problemas de compatibilidad conocidos con el nuevo estándar.

Prueba con Edge (Chromium)

Las marcas de SameSite se establecen en la página de edge://flags/#same-site-by-default-cookies. No se detectaron problemas de compatibilidad con Edge Chromium.

Prueba con Electron

Las versiones de Electron incluyen versiones anteriores de Chromium. Por ejemplo, la versión de Electron usada por  Teams es Chromium 66, que exhibe el comportamiento anterior. Realice sus propias pruebas de compatibilidad con la versión de Electron que use el producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Muestra Documento
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 3.1 cookie

El ejemplo siguiente se puede descargar y probar:

Muestra Documento
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 3.1 cookie

Compatibilidad de .NET Core con el atributo sameSite

.NET Core 3.1 y versiones posteriores admiten el estándar de borrador de 2019 para SameSite. Los desarrolladores pueden controlar mediante programación el valor del mismo atributoSite mediante la propiedad HttpCookie.SameSite. Si establece la propiedad SameSite en Strict (Estricto), Lax (Laxo) o None (Ninguno), esos valores se escribirán en la red con el cookie. Si se establece en igual a (SameSiteMode)(-1), se indica que no se debe incluir ningún atributo de sameSite en la red con cookie.

var cookieOptions = new CookieOptions
{
    // Set the secure flag, which Chrome's changes will require for SameSite none.
    // Note this will also require you to be running on HTTPS.
    Secure = true,

    // Set the cookie to HTTP only which is good practice unless you really do need
    // to access it client side in scripts.
    HttpOnly = true,

    // Add the SameSite attribute, this will emit the attribute with a value of none.
    // To not emit the attribute at all set
    // SameSite = (SameSiteMode)(-1)
    SameSite = SameSiteMode.None
};

// Add the cookie to the response cookie collection
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);

.NET Core 3.1 y versiones posteriores admiten los valores actualizados de SameSite y agregan un valor de enumeración adicional, SameSiteMode.Unspecified a la enumeración SameSiteMode. Este nuevo valor indica que no se debe enviar sameSite con cookie.

Uso de API con SameSite

HttpContext.Response.Cookies.Append tiene Unspecified como valor predeterminado, lo que significa que no se ha agregado ningún atributo SameSite al cookie y el cliente usará su comportamiento predeterminado (Lax para los nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor de SameSite cookie a SameSiteMode.Lax:

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

Todos los componentes de ASP.NET Core que emiten cookies sustituyen los valores predeterminados anteriores con la configuración adecuada para sus escenarios. Los valores predeterminados anteriores invalidados no han cambiado.

Componente cookie Valor predeterminado
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Autenticación de Cookie CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 y versiones posteriores proporciona la siguiente compatibilidad con SameSite:

  • Vuelve a definir el comportamiento de SameSiteMode.None para emitir SameSite=None
  • Agrega un nuevo valor SameSiteMode.Unspecified para omitir el atributo SameSite.
  • Todas las API de cookies tienen como valor predeterminado Unspecified. Algunos componentes que usan cookies establecen valores más específicos para sus escenarios. Consulte la tabla anterior para obtener ejemplos.

En ASP.NET Core 3.0 y versiones posteriores se cambiaron los valores predeterminados de SameSite para evitar conflictos con valores predeterminados de cliente incoherentes. Las siguientes API han cambiado el valor predeterminado de SameSiteMode.Lax a -1 para evitar la emisión de un atributo SameSite para estas cookies:

Historial y cambios

La compatibilidad con SameSite se implementó por primera vez en ASP.NET Core en la versión 2.0 con el estándar de borrador de 2016. El estándar de 2016 era opcional. ASP.NET Core optó por participar estableciendo varias cookies en Lax de manera predeterminada. Después de encontrar varios problemas con la autenticación, se deshabilitó la mayoría del uso de SameSite.

Las revisiones se emitieron en noviembre de 2019 para actualizar del estándar de 2016 al estándar de 2019. El borrador de 2019 de la especificación SameSite:

  • No es compatible con el borrador de 2016. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.
  • Especifica que las cookies se tratan como SameSite=Lax de manera predeterminada.
  • Especifica que las cookies que afirman explícitamente SameSite=None para habilitar la entrega entre sitios deben estar marcadas como Secure. None es una nueva entrada para no participar.
  • Es compatible con las revisiones emitidas para ASP.NET Core 2.1, 2.2 y 3.0. ASP.NET Core 3.1 tiene compatibilidad adicional con SameSite.
  • Está previsto que Chrome lo habilite de manera predeterminada en febrero de 2020. Los exploradores empezaron a pasar a este estándar en 2019.

API afectadas por el cambio del estándar de borrador de SameSite de 2016 al estándar de borrador de 2019

Compatibilidad con exploradores más antiguos

El estándar de SameSite de 2016 obligaba a tratar los valores desconocidos como valores de SameSite=Strict. Las aplicaciones a las que se accede desde exploradores antiguos que admiten el estándar de SameSite de 2016 pueden interrumpirse cuando obtienen una propiedad de SameSite con un valor de None. Las aplicaciones web deben implementar la detección de navegadores si pretenden ser compatibles con navegadores antiguos. ASP.NET Core no implementa la detección del explorador porque los valores de User-Agents son muy volátiles y cambian con frecuencia. Un punto de extensión en Microsoft.AspNetCore.CookiePolicy permite conectar la lógica específica de User-Agent.

En Startup.Configure, agregue código que llame a UseCookiePolicy antes de llamar a UseAuthentication o a cualquier método que escriba cookies:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

En Startup.ConfigureServices, agregue código similar al siguiente:

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);
    });

    services.AddRazorPages();
}

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

En el ejemplo anterior, MyUserAgentDetectionLib.DisallowsSameSiteNone es una biblioteca proporcionada por el usuario que detecta si el agente de usuario no admite None de SameSite:

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

El siguiente código muestra un método DisallowsSameSiteNone de ejemplo:

Advertencia

El código siguiente es solo para demostración:

  • No debería considerarse completo.
  • No se mantiene ni tiene soporte.
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(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;
}

Probar aplicaciones para problemas de SameSite

Las aplicaciones que interactúan con sitios remotos, por ejemplo mediante inicio de sesión de terceros, necesitan:

Probar las aplicaciones web usando una versión cliente que pueda incorporarse al nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevas marcas de características de participación que se pueden usar para las pruebas. Una vez que la aplicación aplique las revisiones de SameSite, pruébela con versiones de cliente anteriores, especialmente Safari. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.

Prueba con Chrome

Chrome 78+ proporciona resultados engañosos porque tiene una mitigación temporal en su lugar. La mitigación temporal de Chrome 78+ permite cookies de menos de dos minutos de antigüedad. Chrome 76 o 77 con las marcas de prueba adecuadas habilitadas proporciona resultados más precisos. Para probar el nuevo comportamiento, cambie chrome://flags/#same-site-by-default-cookies a Habilitado. Se ha informado de que las versiones más antiguas de Chrome (75 e inferiores) fallan con la nueva configuración de None. Consulte Compatibilidad con exploradores más antiguos en este documento.

Google no hace que las versiones de Chrome anteriores estén disponibles. Siga las instrucciones que aparecen en Descargar Chromium para probar versiones anteriores de Chrome. No descargue Chrome desde los vínculos proporcionados mediante la búsqueda de versiones anteriores de Chrome.

A partir de la versión de Canary 80.0.3975.0, la mitigación temporal Lax+POST se puede deshabilitar con fines de prueba con la nueva marca --enable-features=SameSiteDefaultChecksMethodRigorously para permitir la prueba de sitios y servicios en el estado final de la característica en la que se ha quitado la mitigación. Para más información, consulte las Actualizaciones de SameSite de The Chromium Projects

Prueba con Safari

Safari 12 aplica estrictamente el proyecto anterior y falla cuando el nuevo valor None está en un cookie. None se evita a través del código de detección del explorador Compatibilidad con exploradores más antiguos en este documento. Pruebe los inicios de sesión de estilo del sistema operativo basados en Safari 12, Safari 13 y WebKit mediante MSAL, ADAL o cualquier biblioteca que esté usando. El problema depende de la versión del sistema operativo subyacente. Se sabe que OSX Mojave (10.14) e iOS 12 tienen problemas de compatibilidad con el nuevo comportamiento de SameSite. Actualizar el sistema operativo a OSX Catalina (10.15) o iOS 13 soluciona el problema. Safari no tiene actualmente una marca de participación para probar el comportamiento de la nueva especificación.

Prueba con Firefox

La compatibilidad de Firefox con el nuevo estándar puede probarse en la versión 68+ accediendo a la página about:config con la marca de características network.cookie.sameSite.laxByDefault. No ha habido informes de problemas de compatibilidad con versiones anteriores de Firefox.

Prueba con el explorador Edge

Edge admite el antiguo estándar de SameSite. La versión 44 de Edge no tiene problemas de compatibilidad conocidos con el nuevo estándar.

Prueba con Edge (Chromium)

Las marcas de SameSite se establecen en la página de edge://flags/#same-site-by-default-cookies. No se detectaron problemas de compatibilidad con Edge Chromium.

Prueba con Electron

Las versiones de Electron incluyen versiones anteriores de Chromium. Por ejemplo, la versión de Electron usada por  Teams es Chromium 66, que exhibe el comportamiento anterior. Realice sus propias pruebas de compatibilidad con la versión de Electron que use el producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Muestra Documento
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 3.1 cookie

Los ejemplos siguientes se pueden descargar y probar:

Muestra Documento
.NET Core MVC Ejemplo cookie de SameSite de MVC de ASP.NET Core 2.1
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 2.1 cookie

Cambios de comportamiento de revisión de diciembre

El cambio de comportamiento específico para .NET Framework y .NET Core 2.1 es cómo interpreta el valor None la propiedad SameSite. Antes de la revisión, un valor de None significa "No emitir el atributo en absoluto", después de la revisión significa "Emitir el atributo con un valor de None". Después de la revisión, un valor SameSite de (SameSiteMode)(-1) hace que el atributo no se emita.

El valor predeterminado de SameSite para la autenticación de formularios y las cookies de estado de sesión se cambió de None a Lax.

Uso de API con SameSite

HttpContext.Response.Cookies.Append tiene Unspecified como valor predeterminado, lo que significa que no se ha agregado ningún atributo SameSite al cookie y el cliente usará su comportamiento predeterminado (Lax para los nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor de SameSite cookie a SameSiteMode.Lax:

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

Todos los componentes de ASP.NET Core que emiten cookies sustituyen los valores predeterminados anteriores con la configuración adecuada para sus escenarios. Los valores predeterminados anteriores invalidados no han cambiado.

Componente cookie Valor predeterminado
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Autenticación de Cookie CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

Historial y cambios

La compatibilidad con SameSite se implementó por primera vez en ASP.NET Core en la versión 2.0 con el estándar de borrador de 2016. El estándar de 2016 era opcional. ASP.NET Core optó por participar estableciendo varias cookies en Lax de manera predeterminada. Después de encontrar varios problemas con la autenticación, se deshabilitó la mayoría del uso de SameSite.

Las revisiones se emitieron en noviembre de 2019 para actualizar del estándar de 2016 al estándar de 2019. El borrador de 2019 de la especificación SameSite:

  • No es compatible con el borrador de 2016. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.
  • Especifica que las cookies se tratan como SameSite=Lax de manera predeterminada.
  • Especifica que las cookies que afirman explícitamente SameSite=None para habilitar la entrega entre sitios deben estar marcadas como Secure. None es una nueva entrada para no participar.
  • Es compatible con las revisiones emitidas para ASP.NET Core 2.1, 2.2 y 3.0. ASP.NET Core 3.1 tiene compatibilidad adicional con SameSite.
  • Está previsto que Chrome lo habilite de manera predeterminada en febrero de 2020. Los exploradores empezaron a pasar a este estándar en 2019.

API afectadas por el cambio del estándar de borrador de SameSite de 2016 al estándar de borrador de 2019

Compatibilidad con exploradores más antiguos

El estándar de SameSite de 2016 obligaba a tratar los valores desconocidos como valores de SameSite=Strict. Las aplicaciones a las que se accede desde exploradores antiguos que admiten el estándar de SameSite de 2016 pueden interrumpirse cuando obtienen una propiedad de SameSite con un valor de None. Las aplicaciones web deben implementar la detección de navegadores si pretenden ser compatibles con navegadores antiguos. ASP.NET Core no implementa la detección del explorador porque los valores de User-Agents son muy volátiles y cambian con frecuencia. Un punto de extensión en Microsoft.AspNetCore.CookiePolicy permite conectar la lógica específica de User-Agent.

En Startup.Configure, agregue código que llame a UseCookiePolicy antes de llamar a UseAuthentication o a cualquier método que escriba cookies:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

En Startup.ConfigureServices, agregue código similar al siguiente:

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

    services.AddRazorPages();
}

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = (SameSiteMode)(-1);
        }

    }
}

En el ejemplo anterior, MyUserAgentDetectionLib.DisallowsSameSiteNone es una biblioteca proporcionada por el usuario que detecta si el agente de usuario no admite None de SameSite:

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

El siguiente código muestra un método DisallowsSameSiteNone de ejemplo:

Advertencia

El código siguiente es solo para demostración:

  • No debería considerarse completo.
  • No se mantiene ni tiene soporte.
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(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;
}

Probar aplicaciones para problemas de SameSite

Las aplicaciones que interactúan con sitios remotos, por ejemplo mediante inicio de sesión de terceros, necesitan:

Probar las aplicaciones web usando una versión cliente que pueda incorporarse al nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevas marcas de características de participación que se pueden usar para las pruebas. Una vez que la aplicación aplique las revisiones de SameSite, pruébela con versiones de cliente anteriores, especialmente Safari. Para más información, vea Compatibilidad con exploradores más antiguos en este documento.

Prueba con Chrome

Chrome 78+ proporciona resultados engañosos porque tiene una mitigación temporal en su lugar. La mitigación temporal de Chrome 78+ permite cookies de menos de dos minutos de antigüedad. Chrome 76 o 77 con las marcas de prueba adecuadas habilitadas proporciona resultados más precisos. Para probar el nuevo comportamiento, cambie chrome://flags/#same-site-by-default-cookies a Habilitado. Se ha informado de que las versiones más antiguas de Chrome (75 e inferiores) fallan con la nueva configuración de None. Consulte Compatibilidad con exploradores más antiguos en este documento.

Google no hace que las versiones de Chrome anteriores estén disponibles. Siga las instrucciones que aparecen en Descargar Chromium para probar versiones anteriores de Chrome. No descargue Chrome desde los vínculos proporcionados mediante la búsqueda de versiones anteriores de Chrome.

A partir de la versión de Canary 80.0.3975.0, la mitigación temporal Lax+POST se puede deshabilitar con fines de prueba con la nueva marca --enable-features=SameSiteDefaultChecksMethodRigorously para permitir la prueba de sitios y servicios en el estado final de la característica en la que se ha quitado la mitigación. Para más información, consulte las Actualizaciones de SameSite de The Chromium Projects

Prueba con Safari

Safari 12 aplica estrictamente el proyecto anterior y falla cuando el nuevo valor None está en un cookie. None se evita a través del código de detección del explorador Compatibilidad con exploradores más antiguos en este documento. Pruebe los inicios de sesión de estilo del sistema operativo basados en Safari 12, Safari 13 y WebKit mediante MSAL, ADAL o cualquier biblioteca que esté usando. El problema depende de la versión del sistema operativo subyacente. Se sabe que OSX Mojave (10.14) e iOS 12 tienen problemas de compatibilidad con el nuevo comportamiento de SameSite. Actualizar el sistema operativo a OSX Catalina (10.15) o iOS 13 soluciona el problema. Safari no tiene actualmente una marca de participación para probar el comportamiento de la nueva especificación.

Prueba con Firefox

La compatibilidad de Firefox con el nuevo estándar puede probarse en la versión 68+ accediendo a la página about:config con la marca de características network.cookie.sameSite.laxByDefault. No ha habido informes de problemas de compatibilidad con versiones anteriores de Firefox.

Prueba con el explorador Edge

Edge admite el antiguo estándar de SameSite. La versión 44 de Edge no tiene problemas de compatibilidad conocidos con el nuevo estándar.

Prueba con Edge (Chromium)

Las marcas de SameSite se establecen en la página de edge://flags/#same-site-by-default-cookies. No se detectaron problemas de compatibilidad con Edge Chromium.

Prueba con Electron

Las versiones de Electron incluyen versiones anteriores de Chromium. Por ejemplo, la versión de Electron usada por  Teams es Chromium 66, que exhibe el comportamiento anterior. Realice sus propias pruebas de compatibilidad con la versión de Electron que use el producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Muestra Documento
.NET Core MVC Ejemplo cookie de SameSite de MVC de ASP.NET Core 2.1
Razor Pages de .NET Core Ejemplo de SameSite de Razor Pages de ASP.NET Core 2.1 cookie