Trabajar con sameSite cookie en ASP.NET Core

Por Rick Anderson

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

  • CookieLas s sin encabezado SameSite se tratan SameSite=Lax como de forma predeterminada.
  • SameSite=None debe usarse para permitir el uso entre cookie sitios.
  • Cookies que la SameSite=None aserción también debe marcarse como Secure .
  • Las aplicaciones que <iframe> usan pueden experimentar problemas con o porque se tratan como sameSite=Lax sameSite=Strict cookie <iframe> escenarios entre sitios.
  • El estándar SameSite=None de 2016 no permite el valor y hace que algunas implementaciones cookie traten tales como SameSite=Strict . Consulte Compatibilidad con exploradores anteriores en este documento.

La SameSite=Lax configuración funciona para la mayoría de las cookie aplicaciones. Algunas formas de autenticación como OpenID Conectar (OIDC) y WS-Federation tienen como valor predeterminado redireccionamientos basados en POST. Las redirecciones basadas en POST desencadenan las protecciones del explorador 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 diferencias en la forma en que fluye la solicitud.

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

SameSite y Identity

IdentityASP.NET Core en gran medida no se ven afectados por cookie SameSite, excepto en escenarios avanzados como IFrames o la OpenIdConnect integración.

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

Código de ejemplo de prueba de SameSite

El ejemplo siguiente se puede descargar y probar:

Muestra Documento
Páginas de .NET Razor Core ASP.NET Core ejemplo Razor sameSite de páginas 3.1 cookie

Compatibilidad de .NET Core con el atributo sameSite

.NET Core 2.2 y versiones posteriores admiten el borrador estándar de 2019 para SameSite desde el lanzamiento de actualizaciones en diciembre de 2019. Los desarrolladores pueden controlar mediante programación el valor del atributo sameSite mediante la HttpCookie.SameSite propiedad . Al establecer la propiedad en Strict, Lax o None, esos valores SameSite se escriben en la red con cookie . Si se establece en , se indica que no se debe incluir ningún (SameSiteMode)(-1) atributo sameSite en la red 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.
    // 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 de SameSite actualizados y agregan un valor de enumeración adicional a SameSiteMode.Unspecified SameSiteMode la enumeración. Este nuevo valor indica que no se debe enviar sameSite con 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 la SameSite propiedad interpreta el None valor. Antes de la revisión, un valor de significaba "No emitir el atributo en absoluto", después de la revisión significa None "Emit the attribute with a value of None ". Después de la SameSite revisión, un valor de hace que el atributo (SameSiteMode)(-1) no se emita.

El valor predeterminado de SameSite para la autenticación de formularios y los cookie estados de sesión se ha cambiado de a None Lax .

Uso de API con SameSite

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

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

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

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

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

  • Redefine el comportamiento de SameSiteMode.None para emitir SameSite=None
  • Agrega un nuevo valor SameSiteMode.Unspecified para omitir el atributo SameSite.
  • Todas cookie las API tienen como valor predeterminado Unspecified . Algunos componentes que usan cookie valores establecidos de s 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 los valores predeterminados de cliente incoherentes. Las siguientes API han cambiado el valor predeterminado de SameSiteMode.Lax a para evitar la emisión de un atributo -1 SameSite para cookie estas:

Historial y cambios

La compatibilidad con SameSite se implementó por primera vez en ASP.NET Core versión 2.0 con el borrador del estándar de 2016. El estándar de 2016 se ha optado por participar. ASP.NET Core participar estableciendo varios en cookie de Lax forma 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 obtener más información, vea Compatibilidad con exploradores anteriores en este documento.
  • Especifica que cookie los se tratan como de forma SameSite=Lax predeterminada.
  • Especifica que los que declaran explícitamente para habilitar la entrega entre sitios deben cookie SameSite=None marcarse como Secure . None es una nueva entrada que se va a rechazar.
  • 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á programado que Chrome lo habilite de forma 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 borrador estándar de 2019

Compatibilidad con exploradores más antiguos

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

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

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;
        }
    }
}
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, es una biblioteca proporcionada por el usuario que detecta si el agente de usuario no MyUserAgentDetectionLib.DisallowsSameSiteNone admite SameSite None :

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

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

Advertencia

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

  • No se debe considerar 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;
}

Probar aplicaciones para problemas de SameSite

Las aplicaciones que interactúan con sitios remotos, como a través del inicio de sesión de terceros, deben:

Pruebe las aplicaciones web con una versión de cliente que pueda participar en el nuevo comportamiento de SameSite. Chrome, Firefox y Chromium Edge tienen nuevas marcas de características de suscripción que se pueden usar para las pruebas. Una vez que la aplicación aplique las revisiones de SameSite, pruebe con versiones de cliente anteriores, especialmente Safari. Para obtener más información, vea Compatibilidad con exploradores anteriores 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+ cookie permite una antigüedad inferior a dos minutos. Chrome 76 o 77 con las marcas de prueba adecuadas habilitadas proporciona resultados más precisos. Para probar el nuevo comportamiento de SameSite, chrome://flags/#same-site-by-default-cookies cambie a Habilitado. Se notifica que las versiones anteriores de Chrome (75 y versiones posteriores) no se pueden usar con la nueva None configuración. Consulte Compatibilidad con exploradores anteriores en este documento.

Google no hace que las versiones anteriores de Chrome estén disponibles. Siga las instrucciones de 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 canary, la mitigación temporal Lax+POST se puede deshabilitar con fines de prueba mediante la nueva marca para permitir pruebas de sitios y servicios en el estado final de la característica donde se ha quitado la 80.0.3975.0 --enable-features=SameSiteDefaultChecksMethodRigorously mitigación. Para obtener más información, vea The Chromium Projects SameSite Updates

Prueba con Safari

Safari 12 implementó estrictamente el borrador anterior y genera un error cuando el nuevo None valor está en un cookie . None se evita a través del código de detección del explorador Compatible con exploradores más antiguos de 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 use. 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. La actualización del sistema operativo a OSX Catalina (10.15) o iOS 13 corrige el problema. Safari no tiene actualmente una marca de opción para probar el nuevo comportamiento de especificación.

Prueba con Firefox

La compatibilidad de Firefox con el nuevo estándar se puede probar en la versión 68+ si opta por la página con la about:config marca de característica 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 estándar SameSite anterior. La versión 44 de Edge no tiene ningún problema de compatibilidad conocido con el nuevo estándar.

Prueba con Edge (Chromium)

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

Prueba con Electron

Las versiones Electron de incluyen versiones anteriores de Chromium. Por ejemplo, la versión de Electron usada por Teams es Chromium 66, que muestra el comportamiento anterior. Debe realizar sus propias pruebas de compatibilidad con la versión que Electron usa el producto. Consulte Compatibilidad con exploradores anteriores en la sección siguiente.

Recursos adicionales