ASP.NET Core에서 SameSite cookie 작업

작성자: Rick Anderson

SameSite는 CSRF(교차 사이트 요청 위조) 공격에 대한 보호를 제공하도록 설계된 IETF 초안 표준입니다. 원래 2016년에 초안이 발표되었고, 2019년에 업데이트되었습니다. 업데이트된 표준은 이전 표준과 호환되지 않으며 다음과 같은 가장 눈에 띄는 차이점이 있습니다.

  • SameSite 헤더가 없는 Cookie는 기본적으로 SameSite=Lax로 처리됩니다.
  • SameSite=None은 사이트 간 cookie 사용을 허용하는 데 사용해야 합니다.
  • SameSite=None을 어설션하는 Cookie는 Secure로 표시해야 합니다.
  • <iframe>이 사이트 간 시나리오로 처리되기 때문에 <iframe>을 사용하는 애플리케이션은 sameSite=Lax 또는 sameSite=Strictcookie 문제가 발생할 수 있습니다.
  • SameSite=None2016 표준에서 허용되지 않으며 일부 구현에서 cookie를 SameSite=Strict로 처리합니다. 이 문서의 이전 브라우저 지원을 참조하세요.

SameSite=Lax 설정은 대부분의 애플리케이션 cookie에서 작동합니다. OIDC(OpenID Connect) 및 WS-Federation과 같은 일부 형태의 인증은 기본적으로 POST 기반 리디렉션으로 설정됩니다. 사후 기반 리디렉션은 SameSite 브라우저 보호를 트리거하므로 이러한 구성 요소에 대해 SameSite를 사용할 수 없습니다. 대부분의 OAuth 로그인은 요청 진행 방법의 차이로 인해 영향을 받지 않습니다.

cookie를 내보내는 각 ASP.NET Core 구성 요소는 SameSite가 적절한지 결정해야 합니다.

SameSite 및 Identity

ASP.NET Core IdentityIFrames 또는 OpenIdConnect 통합과 같은 고급 시나리오를 제외하고 SameSite cookie의 영향을 크게 받지 않습니다.

Identity를 사용할 때 cookie 공급자 또는 호출 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)을 추가하지 마세요. Identity가 알아서 처리합니다.

SameSite 테스트 샘플 코드

다음 샘플을 다운로드하고 테스트할 수 있습니다.

예제 문서
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages SameSite cookie 샘플

SameSite 특성에 대한 .NET Core 지원

.NET Core는 SameSite에 대한 2019 초안 표준을 지원합니다. 개발자는 HttpCookie.SameSite 속성을 사용하여 SameSite 특성의 값을 프로그래밍 방식으로 제어할 수 있습니다. SameSite 속성을 Strict, Lax 또는 None으로 설정하면 해당 값이 cookie로 네트워크에 기록됩니다. SameSiteMode.Unspecified로 설정하면 cookie를 사용하여 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);
}

SameSite를 통한 API 사용

HttpContext.Response.Cookies.Append는 기본적으로 Unspecified로 설정되어 있으며, SameSite 특성이 cookie에 추가되지 않고 클라이언트는 기본 동작을 사용한다는 의미입니다(새 브라우저의 경우 Lax, 이전 브라우저의 경우에는 없음). 다음 코드에서는 cookie SameSite 값을 SameSiteMode.Lax로 변경하는 방법을 보여 줍니다.

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

cookie를 내보내는 모든 ASP.NET Core 구성 요소는 해당 시나리오에 적합한 설정을 사용하여 이전 기본값을 재정의합니다. 재정의된 이전 기본값은 변경되지 않았습니다.

구성 요소 cookie 기본값
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 인증 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 이상에서는 다음 SameSite 지원을 제공합니다.

  • SameSite=None 내보내기에 대한 SameSiteMode.None의 동작을 다시 정의합니다.
  • SameSite 특성을 생략하는 새 값 SameSiteMode.Unspecified를 추가합니다.
  • 모든 cookie API의 기본값은 Unspecified입니다. cookie를 사용하는 일부 구성 요소는 해당 시나리오에 더 구체적인 값을 설정합니다. 예제는 위의 표를 참조하세요.

ASP.NET Core 3.0 이상에서 SameSite 기본값은 일관되지 않은 클라이언트 기본값과 충돌하지 않도록 변경되었습니다. 다음 API는 기본값을 SameSiteMode.Lax 에서 -1로 변경하여 이러한 cookie에 대한 SameSite 특성을 내보내지 않도록 합니다.

기록 및 변경 내용

SameSite 지원은 2016 초안 표준을 사용하여 ASP.NET Core 2.0에서 처음 구현되었습니다. 2016 표준은 옵트인(opt in)되었습니다. ASP.NET Core는 기본적으로 여러 cookie를 Lax로 설정하여 옵트인(opt in)했습니다. 인증에 몇 가지 문제가 발생한 후에는 대부분의 SameSite 사용이 사용되지 않도록 설정되었습니다.

2016 표준에서 2019 표준으로 업데이트하기 위한 패치가 2019년 11월에 발급되었습니다. SameSite 사양의 2019 초안은 다음과 같습니다.

  • 2016 초안과 이전 버전 호환이 되지 않습니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.
  • cookie가 기본적으로 SameSite=Lax로 처리되도록 지정합니다.
  • 사이트 간 전달을 사용하도록 설정하려면 SameSite=None을 명시적으로 어설션하는 cookie를 Secure로 표시하도록 지정합니다. None은 옵트아웃할 새 항목입니다.
  • ASP.NET Core 2.1, 2.2 및 3.0에 대해 발급된 패치에 의해 지원됩니다. ASP.NET Core 3.1 이상은 추가 SameSite 지원을 제공합니다.
  • 2020년 2월에 기본적으로 Chrome에서 사용하도록 예약됩니다. 브라우저가 2019년의 이 표준으로 이동하기 시작했습니다.

2016 SameSite 초안에서 2019 초안 표준으로 변경되어 영향을 받는 API

이전 브라우저 지원

2016 SameSite 표준에 따라 알 수 없는 값을 SameSite=Strict 값으로 처리합니다. 2016 SameSite 표준을 지원하는 이전 브라우저에서 액세스된 앱은 값이 None인 SameSite 속성을 가져올 때 손상될 수 있습니다. 웹앱은 이전 브라우저를 지원하려는 경우 브라우저 검색을 구현해야 합니다. ASP.NET Core는 User-Agents 값이 매우 휘발성이며 자주 변경되기 때문에 브라우저 검색을 구현하지 않습니다. Microsoft.AspNetCore.CookiePolicy의 확장 지점은 User-Agent 특정 논리를 연결하는 것을 허용합니다.

Program.cs에서 UseAuthentication 또는 cookie를 작성하는 어떤 메서드를 호출하기 전에 UseCookiePolicy를 호출하는 코드를 추가합니다.

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

Program.cs에서 다음 강조 표시된 코드와 유사한 코드를 추가합니다.

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

위의 샘플에서 MyUserAgentDetectionLib.DisallowsSameSiteNone은 사용자 에이전트가 SameSite None을 지원하지 않는지 검색하는 사용자 제공 라이브러리입니다.

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

다음 코드는 샘플 DisallowsSameSiteNone 메서드를 보여 줍니다.

Warning

다음 코드는 데모용으로만 사용할 수 있습니다.

  • 완료되지 않은 것으로 간주됩니다.
  • 유지 관리되거나 지원되지 않습니다.
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;
}

SameSite 문제에 대한 앱 테스트

타사 로그인 등 원격 사이트와 상호 작용하는 앱은 다음 작업을 수행해야 합니다.

새 SameSite 동작을 옵트인(opt in)할 수 있는 클라이언트 버전을 사용하여 웹앱을 테스트합니다. Chrome, Firefox 및 Chromium Edge에는 테스트에 사용할 수 있는 새 옵트인 기능 플래그가 있습니다. 앱이 SameSite 패치를 적용한 후 이전 클라이언트 버전, 특히 Safari를 사용하여 테스트합니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.

Chrome으로 테스트

Chrome 78+는 일시적인 완화를 제공하기 때문에 잘못된 결과를 제공합니다. Chrome 78+ 임시 완화는 cookie를 2분 미만으로 허용합니다. 적절한 테스트 플래그를 사용하도록 설정하면 Chrome 76 또는 77에서 더 정확한 결과를 얻을 수 있습니다. 새 SameSite 동작을 테스트하려면 chrome://flags/#same-site-by-default-cookies사용으로 설정합니다. Chrome(75 이하) 이전 버전이 새 None 설정에 실패했음이 보고됩니다. 이 문서의 이전 브라우저 지원을 참조하세요.

Google은 이전 Chrome 버전을 사용하도록 설정할 수 없습니다. Chromium 다운로드의 지침에 따라 이전 버전의 Chrome을 테스트합니다. 이전 버전의 Chrome을 검색하여 제공된 링크에서 Chrome을 다운로드하지 마세요.

Canary 버전 80.0.3975.0부터 Lax+POST 임시 완화를 새 플래그 --enable-features=SameSiteDefaultChecksMethodRigorously를 사용하여 테스트 목적으로 사용하지 않도록 설정하여 완화가 제거된 기능의 최종 종료 상태에서 사이트 및 서비스를 테스트할 수 있습니다. 자세한 내용은 Chromium 프로젝트 SameSite 업데이트를 참조하세요.

Safari를 사용한 테스트

Safari 12는 이전 초안을 엄격하게 구현했으며 cookie에 새 None 값이 표시되는 경우 실패합니다. None은 이 문서에서 이전 브라우저를 지원하는 브라우저 검색 코드를 통해 방지됩니다. MSAL, ADAL 또는 사용 중인 라이브러리를 사용하여 Safari 12, Safari 13 및 WebKit 기반 OS 스타일 로그인을 테스트합니다. 문제는 기본 OS 버전에 따라 달라집니다. OSX Mojave(10.14) 및 iOS 12는 새 SameSite 동작에 호환성 문제가 있는 것으로 알려져 있습니다. OS를 OSX Catalina(10.15) 또는 iOS 13으로 업그레이드하면 문제가 해결됩니다. Safari에는 현재 새 사양 동작을 테스트하기 위한 옵트인 플래그가 없습니다.

Firefox로 테스트

새 표준에 대한 Firefox 지원은 기능 플래그 network.cookie.sameSite.laxByDefault를 사용하여 about:config 페이지에서 옵트인하여 버전 68 이상에서 테스트할 수 있습니다. 이전 버전의 Firefox와의 호환성 문제에 대한 보고서가 없습니다.

Edge 브라우저를 사용한 테스트

Edge는 이전 SameSite 표준을 지원합니다. Edge 버전 44에는 새로운 표준과의 알려진 호환성 문제가 없습니다.

Edge(Chromium)를 사용한 테스트

SameSite 플래그는 edge://flags/#same-site-by-default-cookies 페이지에 설정되어 있습니다. Edge Chromium에서 호환성 문제가 검색되지 않았습니다.

Electron을 사용한 테스트

Electron 버전에는 이전 버전의 Chromium이 포함되어 있습니다. 예를 들어, Teams에서 사용하는 Electron의 버전은 Chromium 66이며, 이는 이전 동작을 수행합니다. 사용자의 제품에서 사용하는 Electron 버전으로 자체 호환성 테스트를 수행해야 합니다. 다음 섹션에서 이전 브라우저 지원을 참조하세요.

추가 리소스

예제 문서
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages SameSite cookie 샘플

다음 샘플을 다운로드하고 테스트할 수 있습니다.

예제 문서
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages SameSite cookie 샘플

SameSite 특성에 대한 .NET Core 지원

.NET Core 3.1 이상은 SameSite에 대한 2019 초안 표준을 지원합니다. 개발자는 HttpCookie.SameSite 속성을 사용하여 SameSite 특성의 값을 프로그래밍 방식으로 제어할 수 있습니다. SameSite 속성을 Strict, Lax 또는 None으로 설정하면 해당 값이 cookie로 네트워크에 기록됩니다. 이 값을 (SameSiteMode)(-1)로 설정하면 SameSite 특성이 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 이상에서는 업데이트된 SameSite 값을 지원하고 SameSiteMode 열거형에 추가 열거형 값 SameSiteMode.Unspecified를 추가합니다. 이 새 값은 cookie를 사용하여 SameSite를 전송하지 않음을 나타냅니다.

SameSite를 통한 API 사용

HttpContext.Response.Cookies.Append는 기본적으로 Unspecified로 설정되어 있으며, SameSite 특성이 cookie에 추가되지 않고 클라이언트는 기본 동작을 사용한다는 의미입니다(새 브라우저의 경우 Lax, 이전 브라우저의 경우에는 없음). 다음 코드에서는 cookie SameSite 값을 SameSiteMode.Lax로 변경하는 방법을 보여 줍니다.

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

cookie를 내보내는 모든 ASP.NET Core 구성 요소는 해당 시나리오에 적합한 설정을 사용하여 이전 기본값을 재정의합니다. 재정의된 이전 기본값은 변경되지 않았습니다.

구성 요소 cookie 기본값
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 인증 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 이상에서는 다음 SameSite 지원을 제공합니다.

  • SameSite=None 내보내기에 대한 SameSiteMode.None의 동작을 다시 정의합니다.
  • SameSite 특성을 생략하는 새 값 SameSiteMode.Unspecified를 추가합니다.
  • 모든 cookie API의 기본값은 Unspecified입니다. cookie를 사용하는 일부 구성 요소는 해당 시나리오에 더 구체적인 값을 설정합니다. 예제는 위의 표를 참조하세요.

ASP.NET Core 3.0 이상에서 SameSite 기본값은 일관되지 않은 클라이언트 기본값과 충돌하지 않도록 변경되었습니다. 다음 API는 기본값을 SameSiteMode.Lax 에서 -1로 변경하여 이러한 cookie에 대한 SameSite 특성을 내보내지 않도록 합니다.

기록 및 변경 내용

SameSite 지원은 2016 초안 표준을 사용하여 ASP.NET Core 2.0에서 처음 구현되었습니다. 2016 표준은 옵트인(opt in)되었습니다. ASP.NET Core는 기본적으로 여러 cookie를 Lax로 설정하여 옵트인(opt in)했습니다. 인증에 몇 가지 문제가 발생한 후에는 대부분의 SameSite 사용이 사용되지 않도록 설정되었습니다.

2016 표준에서 2019 표준으로 업데이트하기 위한 패치가 2019년 11월에 발급되었습니다. SameSite 사양의 2019 초안은 다음과 같습니다.

  • 2016 초안과 이전 버전 호환이 되지 않습니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.
  • cookie가 기본적으로 SameSite=Lax로 처리되도록 지정합니다.
  • 사이트 간 전달을 사용하도록 설정하려면 SameSite=None을 명시적으로 어설션하는 cookie를 Secure로 표시하도록 지정합니다. None은 옵트아웃할 새 항목입니다.
  • ASP.NET Core 2.1, 2.2 및 3.0에 대해 발급된 패치에 의해 지원됩니다. ASP.NET Core 3.1은 추가 SameSite 지원을 제공합니다.
  • 2020년 2월에 기본적으로 Chrome에서 사용하도록 예약됩니다. 브라우저가 2019년의 이 표준으로 이동하기 시작했습니다.

2016 SameSite 초안에서 2019 초안 표준으로 변경되어 영향을 받는 API

이전 브라우저 지원

2016 SameSite 표준에 따라 알 수 없는 값을 SameSite=Strict 값으로 처리합니다. 2016 SameSite 표준을 지원하는 이전 브라우저에서 액세스된 앱은 값이 None인 SameSite 속성을 가져올 때 손상될 수 있습니다. 웹앱은 이전 브라우저를 지원하려는 경우 브라우저 검색을 구현해야 합니다. ASP.NET Core는 User-Agents 값이 매우 휘발성이며 자주 변경되기 때문에 브라우저 검색을 구현하지 않습니다. Microsoft.AspNetCore.CookiePolicy의 확장 지점은 User-Agent 특정 논리를 연결하는 것을 허용합니다.

Startup.Configure에서 UseAuthentication 또는 cookie를 작성하는 어떤 메서드를 호출하기 전에 UseCookiePolicy를 호출하는 코드를 추가합니다.

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

Startup.ConfigureServices에서 다음과 유사한 코드를 추가합니다.

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

위의 샘플에서 MyUserAgentDetectionLib.DisallowsSameSiteNone은 사용자 에이전트가 SameSite None을 지원하지 않는지 검색하는 사용자 제공 라이브러리입니다.

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

다음 코드는 샘플 DisallowsSameSiteNone 메서드를 보여 줍니다.

Warning

다음 코드는 데모용으로만 사용할 수 있습니다.

  • 완료되지 않은 것으로 간주됩니다.
  • 유지 관리되거나 지원되지 않습니다.
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;
}

SameSite 문제에 대한 앱 테스트

타사 로그인 등 원격 사이트와 상호 작용하는 앱은 다음 작업을 수행해야 합니다.

새 SameSite 동작을 옵트인(opt in)할 수 있는 클라이언트 버전을 사용하여 웹앱을 테스트합니다. Chrome, Firefox 및 Chromium Edge에는 테스트에 사용할 수 있는 새 옵트인 기능 플래그가 있습니다. 앱이 SameSite 패치를 적용한 후 이전 클라이언트 버전, 특히 Safari를 사용하여 테스트합니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.

Chrome으로 테스트

Chrome 78+는 일시적인 완화를 제공하기 때문에 잘못된 결과를 제공합니다. Chrome 78+ 임시 완화는 cookie를 2분 미만으로 허용합니다. 적절한 테스트 플래그를 사용하도록 설정하면 Chrome 76 또는 77에서 더 정확한 결과를 얻을 수 있습니다. 새 SameSite 동작을 테스트하려면 chrome://flags/#same-site-by-default-cookies사용으로 설정합니다. Chrome(75 이하) 이전 버전이 새 None 설정에 실패했음이 보고됩니다. 이 문서의 이전 브라우저 지원을 참조하세요.

Google은 이전 Chrome 버전을 사용하도록 설정할 수 없습니다. Chromium 다운로드의 지침에 따라 이전 버전의 Chrome을 테스트합니다. 이전 버전의 Chrome을 검색하여 제공된 링크에서 Chrome을 다운로드하지 마세요.

Canary 버전 80.0.3975.0부터 Lax+POST 임시 완화를 새 플래그 --enable-features=SameSiteDefaultChecksMethodRigorously를 사용하여 테스트 목적으로 사용하지 않도록 설정하여 완화가 제거된 기능의 최종 종료 상태에서 사이트 및 서비스를 테스트할 수 있습니다. 자세한 내용은 Chromium 프로젝트 SameSite 업데이트를 참조하세요.

Safari를 사용한 테스트

Safari 12는 이전 초안을 엄격하게 구현했으며 cookie에 새 None 값이 표시되는 경우 실패합니다. None은 이 문서에서 이전 브라우저를 지원하는 브라우저 검색 코드를 통해 방지됩니다. MSAL, ADAL 또는 사용 중인 라이브러리를 사용하여 Safari 12, Safari 13 및 WebKit 기반 OS 스타일 로그인을 테스트합니다. 문제는 기본 OS 버전에 따라 달라집니다. OSX Mojave(10.14) 및 iOS 12는 새 SameSite 동작에 호환성 문제가 있는 것으로 알려져 있습니다. OS를 OSX Catalina(10.15) 또는 iOS 13으로 업그레이드하면 문제가 해결됩니다. Safari에는 현재 새 사양 동작을 테스트하기 위한 옵트인 플래그가 없습니다.

Firefox로 테스트

새 표준에 대한 Firefox 지원은 기능 플래그 network.cookie.sameSite.laxByDefault를 사용하여 about:config 페이지에서 옵트인하여 버전 68 이상에서 테스트할 수 있습니다. 이전 버전의 Firefox와의 호환성 문제에 대한 보고서가 없습니다.

Edge 브라우저를 사용한 테스트

Edge는 이전 SameSite 표준을 지원합니다. Edge 버전 44에는 새로운 표준과의 알려진 호환성 문제가 없습니다.

Edge(Chromium)를 사용한 테스트

SameSite 플래그는 edge://flags/#same-site-by-default-cookies 페이지에 설정되어 있습니다. Edge Chromium에서 호환성 문제가 검색되지 않았습니다.

Electron을 사용한 테스트

Electron 버전에는 이전 버전의 Chromium이 포함되어 있습니다. 예를 들어, Teams에서 사용하는 Electron의 버전은 Chromium 66이며, 이는 이전 동작을 수행합니다. 사용자의 제품에서 사용하는 Electron 버전으로 자체 호환성 테스트를 수행해야 합니다. 다음 섹션에서 이전 브라우저 지원을 참조하세요.

추가 리소스

예제 문서
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages SameSite cookie 샘플

다음 샘플을 다운로드하고 테스트할 수 있습니다.

예제 문서
.NET Core MVC ASP.NET Core 2.1 MVC SameSite cookie 샘플
.NET Core Razor Pages ASP.NET Core 2.1 Razor Pages SameSite cookie 샘플

12월 패치 동작 변경 내용

.NET Framework 및 .NET Core 2.1의 특정 동작 변경 내용은 SameSite 속성이 None 값을 해석하는 방법입니다. 패치 이전 None의 값은 “특성을 전혀 내보내지 않음”을 의미하고, 패치 이후에는 “값이 None인 특성을 내보냄”을 의미합니다. 패치 이후에 (SameSiteMode)(-1)SameSite 값은 특성을 내보내지 않습니다.

폼 인증 및 세션 상태 cookie의 기본 SameSite 값이 None에서 Lax로 변경되었습니다.

SameSite를 통한 API 사용

HttpContext.Response.Cookies.Append는 기본적으로 Unspecified로 설정되어 있으며, SameSite 특성이 cookie에 추가되지 않고 클라이언트는 기본 동작을 사용한다는 의미입니다(새 브라우저의 경우 Lax, 이전 브라우저의 경우에는 없음). 다음 코드에서는 cookie SameSite 값을 SameSiteMode.Lax로 변경하는 방법을 보여 줍니다.

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

cookie를 내보내는 모든 ASP.NET Core 구성 요소는 해당 시나리오에 적합한 설정을 사용하여 이전 기본값을 재정의합니다. 재정의된 이전 기본값은 변경되지 않았습니다.

구성 요소 cookie 기본값
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 인증 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

기록 및 변경 내용

SameSite 지원은 2016 초안 표준을 사용하여 ASP.NET Core 2.0에서 처음 구현되었습니다. 2016 표준은 옵트인(opt in)되었습니다. ASP.NET Core는 기본적으로 여러 cookie를 Lax로 설정하여 옵트인(opt in)했습니다. 인증에 몇 가지 문제가 발생한 후에는 대부분의 SameSite 사용이 사용되지 않도록 설정되었습니다.

2016 표준에서 2019 표준으로 업데이트하기 위한 패치가 2019년 11월에 발급되었습니다. SameSite 사양의 2019 초안은 다음과 같습니다.

  • 2016 초안과 이전 버전 호환이 되지 않습니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.
  • cookie가 기본적으로 SameSite=Lax로 처리되도록 지정합니다.
  • 사이트 간 전달을 사용하도록 설정하려면 SameSite=None을 명시적으로 어설션하는 cookie를 Secure로 표시하도록 지정합니다. None은 옵트아웃할 새 항목입니다.
  • ASP.NET Core 2.1, 2.2 및 3.0에 대해 발급된 패치에 의해 지원됩니다. ASP.NET Core 3.1은 추가 SameSite 지원을 제공합니다.
  • 2020년 2월에 기본적으로 Chrome에서 사용하도록 예약됩니다. 브라우저가 2019년의 이 표준으로 이동하기 시작했습니다.

2016 SameSite 초안에서 2019 초안 표준으로 변경되어 영향을 받는 API

이전 브라우저 지원

2016 SameSite 표준에 따라 알 수 없는 값을 SameSite=Strict 값으로 처리합니다. 2016 SameSite 표준을 지원하는 이전 브라우저에서 액세스된 앱은 값이 None인 SameSite 속성을 가져올 때 손상될 수 있습니다. 웹앱은 이전 브라우저를 지원하려는 경우 브라우저 검색을 구현해야 합니다. ASP.NET Core는 User-Agents 값이 매우 휘발성이며 자주 변경되기 때문에 브라우저 검색을 구현하지 않습니다. Microsoft.AspNetCore.CookiePolicy의 확장 지점은 User-Agent 특정 논리를 연결하는 것을 허용합니다.

Startup.Configure에서 UseAuthentication 또는 cookie를 작성하는 어떤 메서드를 호출하기 전에 UseCookiePolicy를 호출하는 코드를 추가합니다.

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

Startup.ConfigureServices에서 다음과 유사한 코드를 추가합니다.

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

    }
}

위의 샘플에서 MyUserAgentDetectionLib.DisallowsSameSiteNone은 사용자 에이전트가 SameSite None을 지원하지 않는지 검색하는 사용자 제공 라이브러리입니다.

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

다음 코드는 샘플 DisallowsSameSiteNone 메서드를 보여 줍니다.

Warning

다음 코드는 데모용으로만 사용할 수 있습니다.

  • 완료되지 않은 것으로 간주됩니다.
  • 유지 관리되거나 지원되지 않습니다.
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;
}

SameSite 문제에 대한 앱 테스트

타사 로그인 등 원격 사이트와 상호 작용하는 앱은 다음 작업을 수행해야 합니다.

새 SameSite 동작을 옵트인(opt in)할 수 있는 클라이언트 버전을 사용하여 웹앱을 테스트합니다. Chrome, Firefox 및 Chromium Edge에는 테스트에 사용할 수 있는 새 옵트인 기능 플래그가 있습니다. 앱이 SameSite 패치를 적용한 후 이전 클라이언트 버전, 특히 Safari를 사용하여 테스트합니다. 자세한 내용은 이 문서의 이전 브라우저 지원을 참조하세요.

Chrome으로 테스트

Chrome 78+는 일시적인 완화를 제공하기 때문에 잘못된 결과를 제공합니다. Chrome 78+ 임시 완화는 cookie를 2분 미만으로 허용합니다. 적절한 테스트 플래그를 사용하도록 설정하면 Chrome 76 또는 77에서 더 정확한 결과를 얻을 수 있습니다. 새 SameSite 동작을 테스트하려면 chrome://flags/#same-site-by-default-cookies사용으로 설정합니다. Chrome(75 이하) 이전 버전이 새 None 설정에 실패했음이 보고됩니다. 이 문서의 이전 브라우저 지원을 참조하세요.

Google은 이전 Chrome 버전을 사용하도록 설정할 수 없습니다. Chromium 다운로드의 지침에 따라 이전 버전의 Chrome을 테스트합니다. 이전 버전의 Chrome을 검색하여 제공된 링크에서 Chrome을 다운로드하지 마세요.

Canary 버전 80.0.3975.0부터 Lax+POST 임시 완화를 새 플래그 --enable-features=SameSiteDefaultChecksMethodRigorously를 사용하여 테스트 목적으로 사용하지 않도록 설정하여 완화가 제거된 기능의 최종 종료 상태에서 사이트 및 서비스를 테스트할 수 있습니다. 자세한 내용은 Chromium 프로젝트 SameSite 업데이트를 참조하세요.

Safari를 사용한 테스트

Safari 12는 이전 초안을 엄격하게 구현했으며 cookie에 새 None 값이 표시되는 경우 실패합니다. None은 이 문서에서 이전 브라우저를 지원하는 브라우저 검색 코드를 통해 방지됩니다. MSAL, ADAL 또는 사용 중인 라이브러리를 사용하여 Safari 12, Safari 13 및 WebKit 기반 OS 스타일 로그인을 테스트합니다. 문제는 기본 OS 버전에 따라 달라집니다. OSX Mojave(10.14) 및 iOS 12는 새 SameSite 동작에 호환성 문제가 있는 것으로 알려져 있습니다. OS를 OSX Catalina(10.15) 또는 iOS 13으로 업그레이드하면 문제가 해결됩니다. Safari에는 현재 새 사양 동작을 테스트하기 위한 옵트인 플래그가 없습니다.

Firefox로 테스트

새 표준에 대한 Firefox 지원은 기능 플래그 network.cookie.sameSite.laxByDefault를 사용하여 about:config 페이지에서 옵트인하여 버전 68 이상에서 테스트할 수 있습니다. 이전 버전의 Firefox와의 호환성 문제에 대한 보고서가 없습니다.

Edge 브라우저를 사용한 테스트

Edge는 이전 SameSite 표준을 지원합니다. Edge 버전 44에는 새로운 표준과의 알려진 호환성 문제가 없습니다.

Edge(Chromium)를 사용한 테스트

SameSite 플래그는 edge://flags/#same-site-by-default-cookies 페이지에 설정되어 있습니다. Edge Chromium에서 호환성 문제가 검색되지 않았습니다.

Electron을 사용한 테스트

Electron 버전에는 이전 버전의 Chromium이 포함되어 있습니다. 예를 들어, Teams에서 사용하는 Electron의 버전은 Chromium 66이며, 이는 이전 동작을 수행합니다. 사용자의 제품에서 사용하는 Electron 버전으로 자체 호환성 테스트를 수행해야 합니다. 다음 섹션에서 이전 브라우저 지원을 참조하세요.

추가 리소스

예제 문서
.NET Core MVC ASP.NET Core 2.1 MVC SameSite cookie 샘플
.NET Core Razor Pages ASP.NET Core 2.1 Razor Pages SameSite cookie 샘플