Obsługa plików cookie SameSite w ASP.NET Core

Rick Anderson

SameSite to wersja robocza standardu IETF zaprojektowana w celu zapewnienia ochrony przed atakami fałszowania żądań między witrynami (CSRF). Pierwotnie opracowany w 2016 r. projekt standardu został zaktualizowany w 2019 roku. Zaktualizowany standard nie jest wstecznie kompatybilny z poprzednim standardem, przy czym najbardziej zauważalne różnice są następujące:

  • Pliki cookie bez nagłówka SameSite są domyślnie traktowane jako SameSite=Lax pliki cookie.
  • SameSite=None musi być używany do zezwalania na używanie plików cookie między witrynami.
  • Pliki cookie, które potwierdzają SameSite=None , muszą być również oznaczone jako Secure.
  • Aplikacje, które używają <iframe>, mogą napotkać problemy z plikiem cookie sameSite=Lax lub sameSite=Strict, ponieważ <iframe> jest traktowane jak w scenariuszach międzywitrynowych.
  • Wartość SameSite=None nie jest dozwolona przez standard 2016 i powoduje, że niektóre implementacje traktują takie pliki cookie jak SameSite=Strict. Zobacz Obsługa starszych przeglądarek w tym dokumencie.

To SameSite=Lax ustawienie działa w przypadku większości plików cookie aplikacji. Niektóre formy uwierzytelniania, takie jak OpenID Connect (OIDC) i WS-Federation, domyślnie używają przekierowań opartych na metodzie POST. Przekierowania oparte na metodzie POST wyzwalają ochronę przeglądarki SameSite, więc dla tych składników mechanizm SameSite jest wyłączony. Większość logowań OAuth nie jest dotknięta ze względu na różnice w sposobie przepływu żądań.

Każdy składnik ASP.NET Core, który emituje pliki cookie, musi zdecydować, czy atrybut SameSite jest odpowiedni.

SameSite i tożsamość

ASP.NET Core Identity w dużej mierze nie ma wpływu plików cookie SameSite, z wyjątkiem zaawansowanych scenariuszy, takich jak integracja z IFrames lub integracji z OpenIdConnect.

W przypadku korzystania z Identitynie dodawaj żadnych dostawców plików cookie ani nie wywołuj services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme), Identity zajmuje się tym.

Przykładowy kod testowy SameSite

Można pobrać i przetestować następujący przykład:

Sample Document
Strony Razor przykładowy plik cookie ASP.NET Core 3.1 Razor Pages SameSite

Obsługa platformy .NET dla tego samego atrybutu witryny

Platforma .NET obsługuje wersję roboczą standardu 2019 dla witryny SameSite. Deweloperzy mogą programowo kontrolować wartość atrybutu sameSite przy użyciu właściwości HttpCookie.SameSite. SameSite Ustawienie właściwości na Strict, Laxlub None powoduje zapisanie tych wartości w sieci za pomocą pliku cookie. Ustawienie SameSiteMode.Unspecified oznacza, że wraz z plikiem cookie nie należy wysyłać atrybutu sameSite.

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

Użycie interfejsu API z SameSite

HttpContext.Response.Cookies.Append domyślnie ma wartość Unspecified, co oznacza, że do pliku cookie nie dodano atrybutu SameSite, a klient będzie używał jego domyślnego zachowania (Lax dla nowych przeglądarek, None dla starych). Poniższy kod pokazuje, jak zmienić wartość pliku cookie SameSite na SameSiteMode.Lax:

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

Wszystkie składniki ASP.NET Core, które emitują pliki cookie, zastępują poprzednie wartości domyślne ustawieniami odpowiednimi dla ich scenariuszy. Przesłonięte poprzednie wartości domyślne nie zostały zmienione.

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

program ASP.NET Core 3.1 lub nowszy zapewnia następującą obsługę witryny SameSite:

  • Redefiniuje działanie SameSiteMode.None, aby emitować SameSite=None
  • Dodaje nową wartość SameSiteMode.Unspecified , aby pominąć atrybut SameSite.
  • Domyślnie wszystkie interfejsy API plików cookie są ustawione na Unspecified. Niektóre składniki korzystające z plików cookie ustawiają wartości bardziej specyficzne dla ich scenariuszy. Zapoznaj się z powyższą tabelą, aby zapoznać się z przykładami.

W ASP.NET Core 3.0 lub nowszych wartości domyślne SameSite zostały zmienione, aby uniknąć konfliktu z niespójnymi wartościami domyślnymi klienta. Następujące interfejsy API zmieniły wartość domyślną z SameSiteMode.Lax na -1, aby uniknąć emitowania atrybutu SameSite dla tych plików cookie:

Historia i zmiany

Obsługa SameSite została po raz pierwszy zaimplementowana w ASP.NET Core w wersji 2.0 przy użyciu roboczego standardu z 2016 r. Standard 2016 był opt-in. ASP.NET Core domyślnie wymaga wyrażenia zgody przez ustawienie kilku plików cookie w elemencie Lax. Po wystąpieniu kilku problemów z uwierzytelnianiem większość zastosowań atrybutu SameSite została wyłączona.

Łatki zostały wydane w listopadzie 2019 r. w celu zaktualizowania standardu z 2016 r. do standardu z 2019 r. Wersja robocza specyfikacji SameSite z 2019 r.:

  • Nie jest zgodny wstecz z wersją roboczą z 2016 roku. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.
  • Określa, że pliki cookie są domyślnie traktowane jako SameSite=Lax .
  • Określa pliki cookie, które jawnie deklarują SameSite=None, aby umożliwić przesyłanie między witrynami, powinny być oznaczone jako Secure. None to nowa opcja umożliwiająca rezygnację.
  • Jest wspierany za pomocą poprawek wydanych dla ASP.NET Core 2.1, 2.2 i 3.0. Program ASP.NET Core 3.1 lub nowszy ma dodatkową funkcjonalność obsługi SameSite.
  • Ma zostać domyślnie włączone w Chrome w lutym 2020 roku. Przeglądarki zaczęły przechodzić na ten standard w 2019 roku.

Interfejsy API, których dotyczy zmiana z wersji roboczej standardu SameSite z 2016 r. na wersję roboczą standardu SameSite z 2019 r.

Obsługa starszych przeglądarek

Standard SameSite z 2016 r. nakazywał traktowanie nieznanych wartości jako wartości SameSite=Strict. Aplikacje otwierane w starszych przeglądarkach, które obsługują standard SameSite 2016, mogą ulec awarii, gdy otrzymają właściwość SameSite o wartości None. Aplikacje internetowe muszą implementować wykrywanie przeglądarki, jeśli zamierzają obsługiwać starsze przeglądarki. ASP.NET Core nie implementuje wykrywania przeglądarki, ponieważ wartości User-Agent są bardzo niestabilne i często zmieniają się. Punkt rozszerzenia w Microsoft.AspNetCore.CookiePolicy umożliwia podłączanie logiki specyficznej dla agenta użytkownika.

W Program.cspliku dodaj kod wywołujący UseCookiePolicy przed wywołaniem UseAuthentication lub dowolnej metody zapisującej pliki cookie:

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

W Program.cspliku dodaj kod podobny do następującego wyróżnionego kodu:

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

W poprzednim przykładzie jest to biblioteka dostarczana przez użytkownika, która wykrywa, MyUserAgentDetectionLib.DisallowsSameSiteNone czy agent użytkownika nie obsługuje atrybutu SameSite None:

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

Poniższy kod przedstawia przykładową DisallowsSameSiteNone metodę:

Warning

Poniższy kod jest przeznaczony tylko dla pokazu:

  • Nie należy tego uważać za kompletne.
  • Nie jest ona utrzymywana ani obsługiwana.
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;
}

Testowanie aplikacji pod kątem problemów z atrybutem SameSite

Aplikacje, które wchodzą w interakcje ze zdalnymi witrynami, na przykład poprzez logowanie zewnętrzne, muszą:

Przetestuj aplikacje internetowe przy użyciu wersji klienta, która może włączyć nowe działanie SameSite. Chrome, Firefox i Chromium Edge mają nowe flagi funkcji wymagające włączenia, które mogą być używane do testowania. Po zastosowaniu poprawek SameSite w aplikacji przetestuj ją ze starszymi wersjami klienta, zwłaszcza w przeglądarce Safari. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.

Testuj w przeglądarce Chrome

Chrome 78+ daje mylące wyniki, ponieważ ma wdrożone tymczasowe obejście. Tymczasowe środki zaradcze w przeglądarce Chrome 78+ pozwalają na pliki cookie młodsze niż dwie minuty. Chrome 76 lub 77 z włączonymi odpowiednimi flagami testowymi zapewnia dokładniejsze wyniki. Aby przetestować nowe zachowanie SameSite, przełącz chrome://flags/#same-site-by-default-cookies na Włączone. Starsze wersje programu Chrome (75 i starsze) zgłasza się, że nie działają przy nowym ustawieniu None. Zobacz Obsługa starszych przeglądarek w tym dokumencie.

Firma Google nie udostępnia starszych wersji chrome. Postępuj zgodnie z instrukcjami na stronie Pobierz Chromium, aby przetestować starsze wersje przeglądarki Chrome. Nie pobieraj przeglądarki Chrome z linków znalezionych podczas wyszukiwania starszych wersji przeglądarki Chrome.

Począwszy od wersji Canary 80.0.3975.0, tymczasowy mechanizm łagodzący Lax+POST można wyłączyć na potrzeby testowania przy użyciu nowej flagi --enable-features=SameSiteDefaultChecksMethodRigorously, aby umożliwić testowanie witryn i usług w docelowym końcowym stanie funkcji, po usunięciu tego mechanizmu łagodzącego. Aby uzyskać więcej informacji, zobacz Chromium Projects SameSite Updates

Testuj w przeglądarce Safari

Przeglądarka Safari 12 ściśle zaimplementowała poprzednią wersję roboczą i kończy się niepowodzeniem, gdy nowa None wartość znajduje się w pliku cookie. None jest omijane dzięki kodowi wykrywania przeglądarki Obsługa starszych przeglądarek w tym dokumencie. Przetestuj logowania w stylu systemowym w Safari 12, Safari 13 oraz przeglądarkach opartych na WebKit, przy użyciu bibliotek MSAL, ADAL lub dowolnej używanej biblioteki. Problem jest zależny od bazowej wersji systemu operacyjnego. OSX Mojave (10.14) i iOS 12 są znane z problemów ze zgodnością z nowym zachowaniem SameSite. Uaktualnienie systemu operacyjnego do systemu OSX Catalina (10.15) lub iOS 13 rozwiązuje problem. Przeglądarka Safari nie ma obecnie flagi opt-in do testowania nowego zachowania specyfikacji.

Testuj w przeglądarce Firefox

Obsługę przeglądarki Firefox dla nowego standardu można przetestować w wersji 68 lub nowszej, na stronie about:config za pomocą flagi funkcji network.cookie.sameSite.laxByDefault. Nie było zgłoszeń dotyczących problemów ze zgodnością ze starszymi wersjami przeglądarki Firefox.

Testowanie za pomocą przeglądarki Edge

Przeglądarka Edge obsługuje stary standard SameSite. Przeglądarka Edge w wersji 44 nie ma żadnych znanych problemów ze zgodnością z nowym standardem.

Testowanie za pomocą przeglądarki Edge (Chromium)

Flagi SameSite są ustawiane na stronie edge://flags/#same-site-by-default-cookies. Nie odnaleziono żadnych problemów ze zgodnością z przeglądarką Edge Chromium.

Testowanie za pomocą elektronu

Wersje Electron obejmują starsze wersje Chromium. Na przykład wersja Electron używana przez Teams to Chromium 66, która wykazuje starsze zachowanie. Musisz przeprowadzić własne testy zgodności z wersją używanej przez produkt Electron. Zobacz Obsługa starszych przeglądarek w poniższej sekcji.

Dodatkowe zasoby

Sample Document
Strony Razor przykładowy plik cookie ASP.NET Core 3.1 Razor Pages SameSite

Można pobrać i przetestować następujący przykład:

Sample Document
Strony Razor przykładowy plik cookie ASP.NET Core 3.1 Razor Pages SameSite

Obsługa platformy .NET Core dla atrybutu SameSite

Program .NET Core 3.1 lub nowszy obsługuje wersję roboczą standardu 2019 dla witryny SameSite. Deweloperzy mogą programowo kontrolować wartość atrybutu sameSite przy użyciu właściwości HttpCookie.SameSite. Ustawienie właściwości SameSite na Strict, Lax lub None powoduje zapisaniem tych wartości w pliku cookie przesyłanym przez sieć. Ustawienie go na (SameSiteMode)(-1) oznacza, że nie należy dołączać do pliku cookie atrybutu sameSite

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

Platforma .NET Core 3.1 lub nowsza obsługuje zaktualizowane wartości SameSite i dodaje nową wartość SameSiteMode.Unspecified do wyliczenia SameSiteMode. Ta nowa wartość wskazuje, że z plikiem cookie nie należy wysyłać sameSite.

Użycie interfejsu API z SameSite

HttpContext.Response.Cookies.Append domyślnie ma wartość Unspecified, co oznacza, że do pliku cookie nie dodano atrybutu SameSite, a klient będzie używał jego domyślnego zachowania (Lax dla nowych przeglądarek, None dla starych). Poniższy kod pokazuje, jak zmienić wartość pliku cookie SameSite na SameSiteMode.Lax:

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

Wszystkie składniki ASP.NET Core, które emitują pliki cookie, zastępują poprzednie wartości domyślne ustawieniami odpowiednimi dla ich scenariuszy. Przesłonięte poprzednie wartości domyślne nie zostały zmienione.

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

program ASP.NET Core 3.1 lub nowszy zapewnia następującą obsługę witryny SameSite:

  • Redefiniuje działanie SameSiteMode.None, aby emitować SameSite=None
  • Dodaje nową wartość SameSiteMode.Unspecified , aby pominąć atrybut SameSite.
  • Domyślnie wszystkie interfejsy API plików cookie są ustawione na Unspecified. Niektóre składniki korzystające z plików cookie ustawiają wartości bardziej specyficzne dla ich scenariuszy. Zapoznaj się z powyższą tabelą, aby zapoznać się z przykładami.

W ASP.NET Core 3.0 lub nowszych wartości domyślne SameSite zostały zmienione, aby uniknąć konfliktu z niespójnymi wartościami domyślnymi klienta. Następujące interfejsy API zmieniły wartość domyślną z SameSiteMode.Lax na -1, aby uniknąć emitowania atrybutu SameSite dla tych plików cookie:

Historia i zmiany

Obsługa SameSite została po raz pierwszy zaimplementowana w ASP.NET Core w wersji 2.0 przy użyciu roboczego standardu z 2016 r. Standard 2016 był opt-in. ASP.NET Core domyślnie wymaga wyrażenia zgody przez ustawienie kilku plików cookie w elemencie Lax. Po wystąpieniu kilku problemów z uwierzytelnianiem większość zastosowań atrybutu SameSite została wyłączona.

Łatki zostały wydane w listopadzie 2019 r. w celu zaktualizowania standardu z 2016 r. do standardu z 2019 r. Wersja robocza specyfikacji SameSite z 2019 r.:

  • Nie jest zgodny wstecz z wersją roboczą z 2016 roku. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.
  • Określa, że pliki cookie są domyślnie traktowane jako SameSite=Lax .
  • Określa pliki cookie, które jawnie deklarują SameSite=None, aby umożliwić przesyłanie między witrynami, powinny być oznaczone jako Secure. None to nowa opcja umożliwiająca rezygnację.
  • Jest wspierany za pomocą poprawek wydanych dla ASP.NET Core 2.1, 2.2 i 3.0. ASP.NET Core 3.1 ma dodatkową obsługę SameSite.
  • Ma zostać domyślnie włączone w Chrome w lutym 2020 roku. Przeglądarki zaczęły przechodzić na ten standard w 2019 roku.

Interfejsy API, których dotyczy zmiana z wersji roboczej standardu SameSite z 2016 r. na wersję roboczą standardu SameSite z 2019 r.

Obsługa starszych przeglądarek

Standard SameSite z 2016 r. nakazywał traktowanie nieznanych wartości jako wartości SameSite=Strict. Aplikacje otwierane w starszych przeglądarkach, które obsługują standard SameSite 2016, mogą ulec awarii, gdy otrzymają właściwość SameSite o wartości None. Aplikacje internetowe muszą implementować wykrywanie przeglądarki, jeśli zamierzają obsługiwać starsze przeglądarki. ASP.NET Core nie implementuje wykrywania przeglądarki, ponieważ wartości User-Agent są bardzo niestabilne i często zmieniają się. Punkt rozszerzenia w Microsoft.AspNetCore.CookiePolicy umożliwia podłączanie logiki specyficznej dla agenta użytkownika.

W Startup.Configurepliku dodaj kod wywołujący UseCookiePolicy przed wywołaniem UseAuthentication lub dowolnej metody zapisującej pliki cookie:

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

W Startup.ConfigureServicespliku dodaj kod podobny do następującego:

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

W poprzednim przykładzie jest to biblioteka dostarczana przez użytkownika, która wykrywa, MyUserAgentDetectionLib.DisallowsSameSiteNone czy agent użytkownika nie obsługuje atrybutu SameSite None:

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

Poniższy kod przedstawia przykładową DisallowsSameSiteNone metodę:

Warning

Poniższy kod jest przeznaczony tylko dla pokazu:

  • Nie należy tego uważać za kompletne.
  • Nie jest ona utrzymywana ani obsługiwana.
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;
}

Testowanie aplikacji pod kątem problemów z atrybutem SameSite

Aplikacje, które wchodzą w interakcje ze zdalnymi witrynami, na przykład poprzez logowanie zewnętrzne, muszą:

Przetestuj aplikacje internetowe przy użyciu wersji klienta, która może włączyć nowe działanie SameSite. Chrome, Firefox i Chromium Edge mają nowe flagi funkcji wymagające włączenia, które mogą być używane do testowania. Po zastosowaniu poprawek SameSite w aplikacji przetestuj ją ze starszymi wersjami klienta, zwłaszcza w przeglądarce Safari. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.

Testuj w przeglądarce Chrome

Chrome 78+ daje mylące wyniki, ponieważ ma wdrożone tymczasowe obejście. Tymczasowe środki zaradcze w przeglądarce Chrome 78+ pozwalają na pliki cookie młodsze niż dwie minuty. Chrome 76 lub 77 z włączonymi odpowiednimi flagami testowymi zapewnia dokładniejsze wyniki. Aby przetestować nowe zachowanie SameSite, przełącz chrome://flags/#same-site-by-default-cookies na Włączone. Starsze wersje programu Chrome (75 i starsze) zgłasza się, że nie działają przy nowym ustawieniu None. Zobacz Obsługa starszych przeglądarek w tym dokumencie.

Firma Google nie udostępnia starszych wersji chrome. Postępuj zgodnie z instrukcjami na stronie Pobierz Chromium, aby przetestować starsze wersje przeglądarki Chrome. Nie pobieraj przeglądarki Chrome z linków znalezionych podczas wyszukiwania starszych wersji przeglądarki Chrome.

Począwszy od wersji Canary 80.0.3975.0, tymczasowy mechanizm łagodzący Lax+POST można wyłączyć na potrzeby testowania przy użyciu nowej flagi --enable-features=SameSiteDefaultChecksMethodRigorously, aby umożliwić testowanie witryn i usług w docelowym końcowym stanie funkcji, po usunięciu tego mechanizmu łagodzącego. Aby uzyskać więcej informacji, zobacz Chromium Projects SameSite Updates

Testuj w przeglądarce Safari

Przeglądarka Safari 12 ściśle zaimplementowała poprzednią wersję roboczą i kończy się niepowodzeniem, gdy nowa None wartość znajduje się w pliku cookie. None jest omijane dzięki kodowi wykrywania przeglądarki Obsługa starszych przeglądarek w tym dokumencie. Przetestuj logowania w stylu systemowym w Safari 12, Safari 13 oraz przeglądarkach opartych na WebKit, przy użyciu bibliotek MSAL, ADAL lub dowolnej używanej biblioteki. Problem jest zależny od bazowej wersji systemu operacyjnego. OSX Mojave (10.14) i iOS 12 są znane z problemów ze zgodnością z nowym zachowaniem SameSite. Uaktualnienie systemu operacyjnego do systemu OSX Catalina (10.15) lub iOS 13 rozwiązuje problem. Przeglądarka Safari nie ma obecnie flagi opt-in do testowania nowego zachowania specyfikacji.

Testuj w przeglądarce Firefox

Obsługę przeglądarki Firefox dla nowego standardu można przetestować w wersji 68 lub nowszej, na stronie about:config za pomocą flagi funkcji network.cookie.sameSite.laxByDefault. Nie było zgłoszeń dotyczących problemów ze zgodnością ze starszymi wersjami przeglądarki Firefox.

Testowanie za pomocą przeglądarki Edge

Przeglądarka Edge obsługuje stary standard SameSite. Przeglądarka Edge w wersji 44 nie ma żadnych znanych problemów ze zgodnością z nowym standardem.

Testowanie za pomocą przeglądarki Edge (Chromium)

Flagi SameSite są ustawiane na stronie edge://flags/#same-site-by-default-cookies. Nie odnaleziono żadnych problemów ze zgodnością z przeglądarką Edge Chromium.

Testowanie za pomocą elektronu

Wersje Electron obejmują starsze wersje Chromium. Na przykład wersja Electron używana przez Teams to Chromium 66, która wykazuje starsze zachowanie. Musisz przeprowadzić własne testy zgodności z wersją używanej przez produkt Electron. Zobacz Obsługa starszych przeglądarek w poniższej sekcji.

Dodatkowe zasoby

Sample Document
Strony Razor przykładowy plik cookie ASP.NET Core 3.1 Razor Pages SameSite

Można pobrać i przetestować następujące przykłady:

Sample Document
MVC Przykład pliku cookie SameSite w ASP.NET Core 2.1 MVC
Strony Razor przykład ASP.NET Core 2.1 Razor Pages dotyczący plików cookie SameSite

Zmiany zachowania poprawki z grudnia

Konkretna zmiana zachowania dla platform .NET Framework i .NET Core 2.1 polega na tym, jak właściwość SameSite interpretuje wartość None. Przed poprawką wartość None oznaczała „Nie emituj atrybutu w ogóle”, po poprawce oznacza to „Emituj atrybut z wartością None”. Po poprawce wartość SameSite wynosząca (SameSiteMode)(-1) powoduje, że atrybut nie jest emitowany.

Domyślna wartość SameSite dla plików cookie uwierzytelniania formularzy i stanu sesji została zmieniona z None na Lax.

Użycie interfejsu API z SameSite

HttpContext.Response.Cookies.Append domyślnie ma wartość Unspecified, co oznacza, że do pliku cookie nie dodano atrybutu SameSite, a klient będzie używał jego domyślnego zachowania (Lax dla nowych przeglądarek, None dla starych). Poniższy kod pokazuje, jak zmienić wartość pliku cookie SameSite na SameSiteMode.Lax:

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

Wszystkie składniki ASP.NET Core, które emitują pliki cookie, zastępują poprzednie wartości domyślne ustawieniami odpowiednimi dla ich scenariuszy. Przesłonięte poprzednie wartości domyślne nie zostały zmienione.

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

Historia i zmiany

Obsługa SameSite została po raz pierwszy zaimplementowana w ASP.NET Core w wersji 2.0 przy użyciu roboczego standardu z 2016 r. Standard 2016 był opt-in. ASP.NET Core domyślnie wymaga wyrażenia zgody przez ustawienie kilku plików cookie w elemencie Lax. Po wystąpieniu kilku problemów z uwierzytelnianiem większość zastosowań atrybutu SameSite została wyłączona.

Łatki zostały wydane w listopadzie 2019 r. w celu zaktualizowania standardu z 2016 r. do standardu z 2019 r. Wersja robocza specyfikacji SameSite z 2019 r.:

  • Nie jest zgodny wstecz z wersją roboczą z 2016 roku. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.
  • Określa, że pliki cookie są domyślnie traktowane jako SameSite=Lax .
  • Określa pliki cookie, które jawnie deklarują SameSite=None, aby umożliwić przesyłanie między witrynami, powinny być oznaczone jako Secure. None to nowa opcja umożliwiająca rezygnację.
  • Jest wspierany za pomocą poprawek wydanych dla ASP.NET Core 2.1, 2.2 i 3.0. ASP.NET Core 3.1 ma dodatkową obsługę SameSite.
  • Ma zostać domyślnie włączone w Chrome w lutym 2020 roku. Przeglądarki zaczęły przechodzić na ten standard w 2019 roku.

Interfejsy API, których dotyczy zmiana z wersji roboczej standardu SameSite z 2016 r. na wersję roboczą standardu SameSite z 2019 r.

Obsługa starszych przeglądarek

Standard SameSite z 2016 r. nakazywał traktowanie nieznanych wartości jako wartości SameSite=Strict. Aplikacje otwierane w starszych przeglądarkach, które obsługują standard SameSite 2016, mogą ulec awarii, gdy otrzymają właściwość SameSite o wartości None. Aplikacje internetowe muszą implementować wykrywanie przeglądarki, jeśli zamierzają obsługiwać starsze przeglądarki. ASP.NET Core nie implementuje wykrywania przeglądarki, ponieważ wartości User-Agent są bardzo niestabilne i często zmieniają się. Punkt rozszerzenia w Microsoft.AspNetCore.CookiePolicy umożliwia podłączanie logiki specyficznej dla agenta użytkownika.

W Startup.Configurepliku dodaj kod wywołujący UseCookiePolicy przed wywołaniem UseAuthentication lub dowolnej metody zapisującej pliki cookie:

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

W Startup.ConfigureServicespliku dodaj kod podobny do następującego:

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

    }
}

W poprzednim przykładzie jest to biblioteka dostarczana przez użytkownika, która wykrywa, MyUserAgentDetectionLib.DisallowsSameSiteNone czy agent użytkownika nie obsługuje atrybutu SameSite None:

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

Poniższy kod przedstawia przykładową DisallowsSameSiteNone metodę:

Warning

Poniższy kod jest przeznaczony tylko dla pokazu:

  • Nie należy tego uważać za kompletne.
  • Nie jest ona utrzymywana ani obsługiwana.
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;
}

Testowanie aplikacji pod kątem problemów z atrybutem SameSite

Aplikacje, które wchodzą w interakcje ze zdalnymi witrynami, na przykład poprzez logowanie zewnętrzne, muszą:

Przetestuj aplikacje internetowe przy użyciu wersji klienta, która może włączyć nowe działanie SameSite. Chrome, Firefox i Chromium Edge mają nowe flagi funkcji wymagające włączenia, które mogą być używane do testowania. Po zastosowaniu poprawek SameSite w aplikacji przetestuj ją ze starszymi wersjami klienta, zwłaszcza w przeglądarce Safari. Aby uzyskać więcej informacji, zobacz Obsługa starszych przeglądarek w tym dokumencie.

Testuj w przeglądarce Chrome

Chrome 78+ daje mylące wyniki, ponieważ ma wdrożone tymczasowe obejście. Tymczasowe środki zaradcze w przeglądarce Chrome 78+ pozwalają na pliki cookie młodsze niż dwie minuty. Chrome 76 lub 77 z włączonymi odpowiednimi flagami testowymi zapewnia dokładniejsze wyniki. Aby przetestować nowe zachowanie SameSite, przełącz chrome://flags/#same-site-by-default-cookies na Włączone. Starsze wersje programu Chrome (75 i starsze) zgłasza się, że nie działają przy nowym ustawieniu None. Zobacz Obsługa starszych przeglądarek w tym dokumencie.

Firma Google nie udostępnia starszych wersji chrome. Postępuj zgodnie z instrukcjami na stronie Pobierz Chromium, aby przetestować starsze wersje przeglądarki Chrome. Nie pobieraj przeglądarki Chrome z linków znalezionych podczas wyszukiwania starszych wersji przeglądarki Chrome.

Począwszy od wersji Canary 80.0.3975.0, tymczasowy mechanizm łagodzący Lax+POST można wyłączyć na potrzeby testowania przy użyciu nowej flagi --enable-features=SameSiteDefaultChecksMethodRigorously, aby umożliwić testowanie witryn i usług w docelowym końcowym stanie funkcji, po usunięciu tego mechanizmu łagodzącego. Aby uzyskać więcej informacji, zobacz Chromium Projects SameSite Updates

Testuj w przeglądarce Safari

Przeglądarka Safari 12 ściśle zaimplementowała poprzednią wersję roboczą i kończy się niepowodzeniem, gdy nowa None wartość znajduje się w pliku cookie. None jest omijane dzięki kodowi wykrywania przeglądarki Obsługa starszych przeglądarek w tym dokumencie. Przetestuj logowania w stylu systemowym w Safari 12, Safari 13 oraz przeglądarkach opartych na WebKit, przy użyciu bibliotek MSAL, ADAL lub dowolnej używanej biblioteki. Problem jest zależny od bazowej wersji systemu operacyjnego. OSX Mojave (10.14) i iOS 12 są znane z problemów ze zgodnością z nowym zachowaniem SameSite. Uaktualnienie systemu operacyjnego do systemu OSX Catalina (10.15) lub iOS 13 rozwiązuje problem. Przeglądarka Safari nie ma obecnie flagi opt-in do testowania nowego zachowania specyfikacji.

Testuj w przeglądarce Firefox

Obsługę przeglądarki Firefox dla nowego standardu można przetestować w wersji 68 lub nowszej, na stronie about:config za pomocą flagi funkcji network.cookie.sameSite.laxByDefault. Nie było zgłoszeń dotyczących problemów ze zgodnością ze starszymi wersjami przeglądarki Firefox.

Testowanie za pomocą przeglądarki Edge

Przeglądarka Edge obsługuje stary standard SameSite. Przeglądarka Edge w wersji 44 nie ma żadnych znanych problemów ze zgodnością z nowym standardem.

Testowanie za pomocą przeglądarki Edge (Chromium)

Flagi SameSite są ustawiane na stronie edge://flags/#same-site-by-default-cookies. Nie odnaleziono żadnych problemów ze zgodnością z przeglądarką Edge Chromium.

Testowanie za pomocą elektronu

Wersje Electron obejmują starsze wersje Chromium. Na przykład wersja Electron używana przez Teams to Chromium 66, która wykazuje starsze zachowanie. Musisz przeprowadzić własne testy zgodności z wersją używanej przez produkt Electron. Zobacz Obsługa starszych przeglądarek w poniższej sekcji.

Dodatkowe zasoby

Sample Document
MVC Przykład pliku cookie SameSite w ASP.NET Core 2.1 MVC
Strony Razor przykład ASP.NET Core 2.1 Razor Pages dotyczący plików cookie SameSite