Trabajar 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 retrocompatible 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 debe usarse para permitir el uso de cookies entre sitios.
  • Las cookies que declaran SameSite=None también deben marcarse como Secure.
  • Las aplicaciones que usan <iframe> pueden experimentar problemas con las cookies de sameSite=Lax o sameSite=Strict porque <iframe> se considera en escenarios 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 navegadores antiguos en este documento.

La configuración de SameSite=Lax funciona para la mayoría de las cookies de la aplicación. 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 e Identidad

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

Al usar Identity, no agregue ningún proveedor de cookies ni llame a services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme), Identity se encarga de eso.

Código de ejemplo de prueba de SameSite

El ejemplo siguiente se puede descargar y probar:

Sample Document
Razor Pages ASP.NET Core ejemplo de cookie SameSite de páginas de Razor 3.1

Compatibilidad de .NET con el atributo SameSite

.NET admite el estándar de borrador de 2019 para SameSite. Los desarrolladores pueden controlar mediante programación el valor del atributo sameSite mediante la propiedad HttpCookie.SameSite. Al establecer la propiedad SameSite en Strict, Lax o None, esos valores se escriben en la red junto con la cookie. Establecer en SameSiteMode.Unspecified indica que no se debe enviar el sameSite con la 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 a la cookie y el cliente usará su comportamiento predeterminado (Lax para nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor sameSite de 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 anulan los valores predeterminados anteriores con la configuración adecuada para sus escenarios. Los valores predeterminados anteriores sobrescritos no han cambiado.

Component cookie Default
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Autenticación de cookies 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 o posterior 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 o posterior 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:

Historia 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 se habilitó 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.

Los parches 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 retrocompatible 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 el envío entre sitios deben estar marcadas como Secure. None es una nueva entrada para excluirse.
  • Es compatible con los parches emitidos para ASP.NET Core 2.1, 2.2 y 3.0. ASP.NET Core 3.1 o posterior tiene compatibilidad adicional con SameSite.
  • Está previsto que Chrome lo habilite de manera predeterminada en febrero de 2020. Los navegadores 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 SameSite de 2019

Admitir navegadores antiguos

El estándar SameSite de 2016 obligaba a tratar los valores desconocidos como el valor SameSite=Strict. Las aplicaciones a las que se accede desde exploradores antiguos que admiten el estándar SameSite de 2016 pueden dejar de funcionar cuando obtienen una propiedad 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, agrega 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 SameSite None:

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

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

Warning

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

Aplicaciones de prueba 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 activar el nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevos indicadores de función de activación voluntaria que se pueden usar para las pruebas. Una vez que su aplicación haya aplicado 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+ admite 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 de SameSite, cambie el conmutador 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 ha aplicado estrictamente el proyecto anterior y falla cuando el nuevo valor de None se encuentra en una cookie. Se evita None 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 al estilo de SO 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 opción de activación voluntaria 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 feature flag 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 SameSite se establecen en la página 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 empleada por Teams es Chromium 66, que exhibe el comportamiento anterior. Debe llevar a cabo sus propias pruebas de compatibilidad con la versión de Electron que utilice su producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Sample Document
Razor Pages ASP.NET Core ejemplo de cookie SameSite de Razor Pages 3.1

El ejemplo siguiente se puede descargar y probar:

Sample Document
Razor Pages ASP.NET Core ejemplo de cookie SameSite de Razor Pages 3.1

Compatibilidad de .NET Core con el atributo sameSite

.NET Core 3.1 o posterior admite el estándar de borrador de 2019 para SameSite. Los desarrolladores pueden controlar mediante programación el valor del atributo sameSite mediante la propiedad HttpCookie.SameSite. Si se establece la SameSite propiedad en Strict, Lax o None, esos valores se escriben en la red con la cookie. Establecerlo igual a (SameSiteMode)(-1) indica que no se debe incluir ningún atributo sameSite con la cookie en la solicitud de red.

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 o posterior admiten los valores actualizados de SameSite y agregan un valor adicional a la enumeración SameSiteMode.Unspecified en la enumeración SameSiteMode. Este nuevo valor indica que no se debe enviar el sameSite con la 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 a la cookie y el cliente usará su comportamiento predeterminado (Lax para nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor sameSite de 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 sobrescritos no han cambiado.

Component cookie Default
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Autenticación de cookies 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 o posterior 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 o posterior 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:

Historia 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 de adopción voluntaria. ASP.NET Core se habilitó 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.

Los parches 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 retrocompatible 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 el envío entre sitios deben estar marcadas como Secure. None es una nueva entrada para darse de baja.
  • Recibe soporte mediante los parches emitidos 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 navegadores empezaron a adoptar este estándar en 2019.

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

Compatibilidad con exploradores más antiguos

El estándar de SameSite de 2016 obligaba a tratar los valores desconocidos como valores 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, agrega 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:

Warning

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

  • No debería considerarse completo.
  • No se mantiene ni se admite.
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;
}

Aplicaciones de prueba 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 activar el nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevos indicadores de función de activación voluntaria 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 aplica una mitigación temporal. La mitigación temporal de Chrome 78+ admite 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 el conmutador de comportamiento SameSite 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 navegadores 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 enlaces obtenidos al buscar 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 ha aplicado estrictamente el proyecto anterior y falla cuando el nuevo valor de None se encuentra en una cookie. None se evita a través del código de detección del explorador Compatibilidad con navegadores antiguos en este documento. Pruebe los inicios de sesión de tipo SO 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 un indicador de activación voluntaria 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+ activándola en 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 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 empleada por Teams es Chromium 66, que exhibe el comportamiento anterior. Debe llevar a cabo sus propias pruebas de compatibilidad con la versión de Electron que utilice su producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Sample Document
Razor Pages ASP.NET Core ejemplo de cookie SameSite de páginas de Razor 3.1

Los ejemplos siguientes se pueden descargar y probar:

Sample Document
MVC Ejemplo de cookie de SameSite de ASP.NET Core 2.1 MVC
Razor Pages ASP.NET Core 2.1 Ejemplo de cookie de Razor Pages SameSite

Cambios de comportamiento del parche de diciembre

El cambio de comportamiento específico para .NET Framework y .NET Core 2.1 es cómo la propiedad SameSite interpreta el valor None. Antes del parche, un valor de None significa "No emitir el atributo en absoluto", después del parche significa "Emitir el atributo con un valor de None". Después del parche, 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 a la cookie y el cliente usará su comportamiento predeterminado (Lax para nuevos exploradores, None para los antiguos). En el código siguiente se muestra cómo cambiar el valor sameSite de 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 que se han sobrescrito no han cambiado.

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

Historia 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 se habilitó estableciendo varias cookies de forma predeterminada en Lax. Después de encontrar varios problemas con la autenticación, se deshabilitó la mayoría del uso de SameSite.

Los parches 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 retrocompatible con el borrador de 2016. Para más información, consulte Compatibilidad con navegadores 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 el envío entre sitios deben estar marcadas como Secure. None es una nueva entrada para excluirse.
  • Cuenta con soporte mediante los parches emitidos 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 navegadores empezaron a pasar a este estándar en 2019.

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

Compatibilidad con navegadores antiguos

El estándar de SameSite de 2016 obligaba a tratar los valores desconocidos como valores 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 incorporar lógica específica del User-Agent.

En Startup.Configure, agrega 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 SameSite None:

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

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

Warning

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 habilitar el nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevas flags de función de activación voluntaria que se pueden usar para las pruebas. Una vez que su 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 cuenta con una mitigación temporal. La mitigación temporal de Chrome 78+ admite 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 conmutador del comportamiento SameSite, 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 enlaces encontrados al buscar 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 ha aplicado estrictamente el proyecto anterior y falla cuando el nuevo valor de None se encuentra en una cookie. None se evita a través del código de detección del explorador Compatibilidad con navegadores antiguos en este documento. Pruebe los inicios de sesión de tipo SO 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 opción de activación voluntaria 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+ activando la marca de características network.cookie.sameSite.laxByDefault en la página about:config. 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 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 empleada por Teams es Chromium 66, que exhibe el comportamiento anterior. Debe llevar a cabo sus propias pruebas de compatibilidad con la versión de Electron que utilice su producto. Consulte Compatibilidad con exploradores más antiguos en la sección siguiente.

Recursos adicionales

Sample Document
MVC Ejemplo de cookie de SameSite de ASP.NET Core 2.1 MVC
Razor Pages ASP.NET Core 2.1 Ejemplo de cookie de Razor Pages SameSite