آینده-تغییرات-SameSite-Cookie-در-Asp.Net-و-Asp.net-CoreReviewed by کارشناسان.نت on November 13Rating:5

SameSite یکی از attribute های Http Cookie ها هست که در سال 2016 عملیاتی شده است.این مقاله به سیاست های جدید گوگل در مورد SameSite و راه حل های آن در Asp.Net و Asp.net Core می پردازد.

کاربرد اصلی SameSite برای کاهش ریسک حملات cross site request forgery (CSRF) ، طراحی اولیه این attribute یک قابلیت انتخابی(Optional) بود که با دو مقدار Lax و Strict مقدار دهی می شد ، مقدار Lax مشخص می کرد که کوکی فقط باید در درخواست های درون خود وبسایت ارسال شود و یا در درخواست های GET از یک وبسایت خارجی به درون وبسایت شما ، در مقابل مقدار Strict مشخص می کرد که کوکی تنها از درخواست های درون وبسایت قابل دسترس می باشد.

OpenIdConnect و سایر ابزاری که به ارسال کوکی بین دو سایت مختلف برای عملیات هایی مثل LogIn و LogOut وابسته هستند ، نیازمند تنظیم صحیح این خصیصه در کوکی ها هستند. عدم مقدار دهی این خصیصه ، باعث می شد که به صورت پیش فرض هیچ گونه محدودیتی بر روی کوکی مورد نظر اعمال نشود. اما حالا گوگل استاندارد های خود را بروز کرده است و در ورژن جدید Chrome مقدار جدید "None" برای خصیصه SameSite در نظر گرفته شده است و در صورت عدم مقدار دهی این خصیصه ، مقدار پیش فرض به Lax تغییر کرده است.

تا اینجا کار هیچ مشکلی وجود ندارد و کارها بدون نیاز به تغییر انجام خواهد شد ، اما مشکل مرتبط با مرورگر های قدیمی است که در صورت عدم فهم مقدار خصیصه SameSite ، مقدار را به Strict تفسیر می کنند و در نتیجه ابزار هایی مثل OpenIdConnect به طور کامل از کار می افتند.

اما راهکار چیست ؟

 اول از همه ارتقا به ورژن 4.7.2 برای Asp.Net و حداقل استفاده از ورژن 2.1 برای Asp.Net Core سپس با بررسی agent مرورگر کاربر از قدیمی بودن یا جدید بودن مرورگر اطلاع پیدا کنید تا در صورت عدم پشتیبانی مرورگر کاربر از سیاست های جدید گوگل در مورد SameSite به صورت موردی خودتان مقدار دلخواه را برای خصیصه SameSite تنظیم کنید.

برای ASP.NET 4.7.2 با Microsoft.Owin 4.1.0 می توانیذ از قطعه کد زیر استفاده کنید :

public class SameSiteCookieManager : ICookieManager
{
  private readonly ICookieManager _innerManager;

  public SameSiteCookieManager() : this(new CookieManager())
  {
  }

  public SameSiteCookieManager(ICookieManager innerManager)
  {
    _innerManager = innerManager;
  }

  public void AppendResponseCookie(IOwinContext context, string key, string value,
                                   CookieOptions options)
  {
    CheckSameSite(context, options);
    _innerManager.AppendResponseCookie(context, key, value, options);
  }

  public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
  {
    CheckSameSite(context, options);
    _innerManager.DeleteCookie(context, key, options);
  }

  public string GetRequestCookie(IOwinContext context, string key)
  {
    return _innerManager.GetRequestCookie(context, key);
  }

  private void CheckSameSite(IOwinContext context, CookieOptions options)
  {
    if (options.SameSite == SameSiteMode.None && DisallowsSameSiteNone(context))
    {
        options.SameSite = null;
    }
  }

  public static bool DisallowsSameSiteNone(IOwinContext context)
  {
    // TODO: Use your User Agent library of choice here.
    var userAgent = context.Request.Headers["User-Agent"];
    return userAgent.Contains("BrokenUserAgent") ||
           userAgent.Contains("BrokenUserAgent2")
  }
}

و سپس به تنظیم OpenIdConnect تا از CookieManager جدید استفاده کند ، همانند کد زیر :

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
    // … Your preexisting options … 
    CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});

برای ASP.NET Core از قطعه کد زیر استفاده نمایید :

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        // TODO: Use your User Agent library of choice here.
        if (/* UserAgent doesn’t support new behavior */)
        {
               // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1)
               options.SameSite = SameSiteMode.Unspecified;
         }
    }
}

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

public void Configure(IApplicationBuilder app)
{
    app.UseCookiePolicy(); // Before UseAuthentication or anything else that writes cookies.
    app.UseAuthentication();
    // …
}

اما برای تست agent مرورگر ها ، تیم Azure Active Directory روشی را پیدا کرده است که در اکثر قریب به اتفاق مرورگرهای موجود در بازار ، مرورگرهایی که قادر به تشخیص مقدار جدید نیستند را پیدا می کند ، شما می توانید برای تست agent مرورگر کاربران از قطعه کد زیر استفاده کنید :

 

public static bool DisallowsSameSiteNone(string userAgent)
{
    // 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;
}

البته دقت داشته باشد که این متد تضمین 100 درصد ندارد و شما باید برای سیستم خود ، سایر مرورگر های احتمالی کاربرانتان را شناسایی و به لیست اضافه کنید.برای توضیحات بیشتر به مطلب با عنوان Upcoming SameSite Cookie Changes in ASP.NET and ASP.NET Core مراجعه بفرمایید.