一、Cookie SameSite属性介绍

就像大家已经知道的,一旦设置Cookie之后,在Cookie失效之前浏览器会一直将这个Cookie在后续所有的请求中都传回到Server端。我们的系统会利用Cookie这个特性做很多事情,但通常我们会在Cookie中存放加密的用户身份,在Server端根据此身份检验用户是否有权限进行相应操作。

发送Cookie时,以往浏览器并不检测当前地址栏上的域(Domain)是不是和这个Cookie所属的域是否相同。恶意用户会利用这个问题巧妙设计一个站点,诱导用户点击从而造成跨站点请求伪造攻击(CSRF)。

为了解决这个问题,国际互联网工程任务组(IETF)提出了一个SameSite的草稿标准,Chrome 51开始支持此功能,但从Chrome 80 Stable版本开始启用一个较严格(Lax)的默认设置。

二、什么是跨站点请求伪造攻击(Cross-Site Request Forgery Attack,CSRF)

CSRF攻击简单而言就是恶意用户通过巧妙伪造请求从而盗用合法用户的身份进行恶意操作。

比如你开发了一个非常厉害的系统,系统中某些操作只有特定的人登录之后才有权限使用:

yourdomain.com/snap

[Authorize("Thanos")]
[HttpPost]
public ActionResult Snap()
{
///dangerous, will destroy the world.
}

因为系统要检验身份和权限,除非恶意用户能破解登录系统以Thanos身份登录,否则是没有办法调用这个方法的。

但是恶意用户可以伪造一个像下面这样的页面,恶意用户通过发邮件或者通过跨站点脚本攻击(XSS)等方式诱导具有权限的用户点击页面上的某些Button。如果具有权限的用户刚好已经登录,一旦点击按钮,系统则会以这个用户的身份触发上面危险的操作Snap()。

malicioususer.com/fancypage

...
<form action="yourdomain.com/snap">
<input type="Submit" value="This is cool, click me"/>
</form>
...

当然,微软 ASP.NET是通过AntiForgeryToken来解决这个问题,不过这个不是这篇blog讨论的主题。

三、Cookie的SameSite属性

为了解决上面到的Cookie的安全问题,Chrome从版本51增加了一个新的Cookie属性SameSite, 以控制Cookie是否能在跨站点的情况下传送。

Cookie所属的域名如果和浏览器地址栏中的域名不一致,则认为是跨站点。另外,当你的站点被iframe嵌在第三方站点时也被认为是跨站点。

这个属性有三个属性值:

  1. None

    如果你需要在任意跨站点情况下都使用某个Cookie,则需要将这个Cookie的SameSite设置为None. 但这里需要注意的是一定要同时设置Cookie的Secure,也就是需要使用https访问时才能关闭SameSite功能. 如果没有标明为secure, Chrome 80及以上会拒绝设置这个Cookie。

    set-cookie: samesite=test; path=/; secure; SameSite=None
  2. Strict

    顾名思义,这是严格模式,就是在任何情况下都不允许跨站点发送Cookie。

    这个设置显然是可以解决上面所提到的CSRF问题。因为当访问 malicioususer.com/fancypage 页面时,当前域是 malicioususer.com, 但user点击button提交时的action是指向另外一个域 yourdomain.com,这是两个不同的域,浏览器将不回传yourdomain.com下面的Cookie。这会极大的提高我们系统的安全性。

    但这个严格模式也限制了一些被认为是安全的链接操作,比如:

    1. 你先登录了公司HR系统,假设该系统将所有Cookie的SameSite都设置为strict.
    2. 你用Web邮件系统收到了要求你到HR系统做审批操作的邮件,这封邮件带了一个link,直接链接到HR系统中审批的页面;
    3. 你点击这个link,但因为Cookie被设置为Strict模式,当到达审批页面时,HR系统没有收到任何Cookie,这时会认为你没有登录,而直接跳转到登录页面。在要求不是非常严格的情形下,可以认为这不是我们所期望的行为。因为只是跳到链接指向的页面并不是像POST操作修改数据。这需要通过下面的Lax属性解决这个问题。
  3. Lax

    Lax是比Strict稍宽松的模式,如果我们要允许跨站点链接传Cookie或FORM用GET Method提交时跨站点传Cookie, 则可以将这些Cookie的SameSite设置为Lax. Lax在Chrome 80成为默认设置,Lax既防止了CSRF也确保了正常的跨站点链接,是适合大多数站点的,可以解决上面HR系统安全中提到问题。

    如果你的站点需要被iframe嵌套在第三方站点,这时你还是需要将Cookie设置为None。

    这里也想到一点是,如果你的MVC Action只期望接受POST方法,那么一定要加上HttpPost Attribute,以避免造成意外的安全问题。

四、浏览器兼容性

如下图示目前主流浏览器都已经支持SameSite,虽然 IE 11不支持,但我测试之后发现这个Cookie本身还是没有丢失,只是缺失了安全保护功能。

https://developer.mozilla.org/en-US/docs/Web/HTTP/headers/Set-Cookie#Browser_compatibility

五、如何修改ASP.NET程序

下面总结的步骤是适用于基于ASP.NET开发的系统。微软官方白皮书对这些属性设置做了详细的说明,也可以参考官方白皮书

  1. .NET Framework 4.7.2 或4.8才开始支持SameSite, 在HttpCookie增加了SameSite的属性。所以需要安装.NET Framework 4.7.2以上SDK, 并且需要安装开发电脑和服务器上。

  2. 安装 Windows 2019/11/19累积更新补丁,请见KB Articles that support SameSite in .NET Framework,也需要安装在开发电脑和服务器上。没有安装这个补丁之前,如果SameSite为None, .NET Framework并不输出这个属性到Broswer, 但Chrome 80及以后将未设置默认为Lax,因此造成不一致的行为,所以需要安装这个补丁以明确输出None。

  3. 在Chrome地址栏输入: chrome://flags/, 将下面两项设置为Enabled。开启这两项设置是因为不是所有的Chrome都默认启用了这两项设置,Chrome只是在逐渐将这两项开启到Chrome的user. 所以开发时为了重现问题,最好是显式开启。

    chrome://flags/#same-site-by-default-cookies

    chrome://flags/#cookies-without-same-site-must-be-secure

  4. 修改项目文件属性, Target framework 4.7.2 或4.8。

  5. 根据需要修改web.config对Cookie的SameSite设置。

    <configuration>
    <system.web>
    <httpCookies sameSite="[Strict|Lax|None|Unspecified]" requireSSL="[true|false]" />
    <anonymousIdentification cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
    <authentication>
    <forms cookieSameSite="Lax" requireSSL="false" />
    </authentication>
    <sessionState cookieSameSite="Lax" /> <!-- No config attribute for Secure -->
    <roleManager cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
    <system.web>
    <configuration>

    修改说明:

    • httpCookies节点中的sameSite设置会影响系统中所有未指定sameSite Cookie的值, 但不覆盖forms/sessionState中设置的SameSite属性。

    • forms authentication的sessionState的默认Lax模式应该能满足常规需要, 并且保证网站的安全.

    • 确实需要接受跨站点Cookie, 比如你的网站会嵌套在第三方网站的iframe里面,则需要将相关的Cookie 的SameSite改为None。需要注意的是为None的时候必须要将requireSSL改为true:

      <configuration>
      <system.web>
      <httpCookies sameSite="None" requireSSL="true" />
      <authentication>
      <forms cookieSameSite="None" requireSSL="true" />
      </authentication>
      <sessionState cookieSameSite="None" />
      <roleManager cookieRequireSSL="false" />
      <system.web>
      <configuration>
    • Cookie的SameSite都设置为None之后,需要防范CSRF. ,比如使用AntiForgeryToken

  6. 如果某些Cookie需要使用与web.config中配置的不同SameSite属性,只需要在设置Cookie的时候明确指定其值.

         var cookie = new HttpCookie("samesite", "test");
    cookie.SameSite = SameSiteMode.None;
    cookie.Secure = true;
    Response.Cookies.Add(cookie);
  7. 因为有些老的浏览器并不支持SameSite这个属性,直接输出这个属性会造成老的浏览器忽略这个Cookie而造成Cookie丢失,请见已知的兼容问题. 如果确实需要支持这些老的浏览器,则需要根据user agent来检测浏览器,对于不支持SameSite的浏览器,我们需要将SameSite设置为(SameSiteMode)(-1).

    private void CheckSameSite(HttpContext httpContext, HttpCookie cookie)
    {
    if (cookie.SameSite == SameSiteMode.None)
    {
    var userAgent = httpContext.Request.UserAgent;
    if (BrowserDetection.DisallowsSameSiteNone(userAgent))
    {
    cookie.SameSite = (SameSiteMode)(-1);
    }
    }
    }

    BrowserDetection.DisallowsSameSiteNone()这个方法请见SampleSiteSupport.cs.

对于ASP.NET Core应用,微软也提供了详细的解决方案

六、如何排查SameSite问题

SameSite默认为Lax已经从Chrome 80 Stable正式开始灰度启用,如果一个Cookie SameSite未指定,将会被默认为Lax,这可能会造成网站在某些情况下不能正常工作。

Chrome Developer Tools专门为SameSite问题提供了一个检测工具,在Network tab下有一个选项"Only show requests with SameSite issues". 找到有问题的request之后,可以在Response Headers下面找到哪个Cookie有问题。如下图示,因为我设置了SameSite为None,但没有设置Secure,所以Chrome会拒绝这个Cookie.

set-cookie: samesite=test; path=/; SameSite=None

在调试的时候如果有些跳转操作,为了看到跳转前后的请求记录,可以勾中Preserve log

七、参考

MDN web docs: Set-Cookie

Chrome SameSite Updates

Cookies default to SameSite=Lax

Reject insecure SameSite=None cookies

Work with SameSite cookies in ASP.NET

Work with SameSite cookies in ASP.NET Core

作者:吴秀祥

2020/3/28

Cookie SameSite属性介绍及其在ASP.NET项目中的应用的更多相关文章

  1. win7,vs2010,asp.net项目中修改外部js文件,在调试时加载的还是旧文件

    win7,vs2010,asp.net项目中修改外部js文件,在调试时加载的还是旧文件 我杀过 w3wp.exe和asp.net_state的进程,重启 iis admin的服务,都还是不行. 只是把 ...

  2. ASP.NET项目中引用全局dll

    在ASP.NET项目中,有些dll是全局dll,也就是说,没有放在单个项目的引用中.它们一般存放在如下目录C:\Windows\assembly中 这个时候,我们需要在单个项目中引用他们,应该如何做呢 ...

  3. 如何在ASP.NET 项目中使用Silverlight页面

    闲来无事,想写个网站玩玩,比较懒,不想写太多的样式来美化,看中了Silverlight,样式布局比较省事,但是又不想全部都用Silverlight 来写,所以才有此一文. 其实Silverlight最 ...

  4. VS的ASP.NET项目中cshtml关键词出错 红线,当前上下文中不存在名称

    [参考]VS的ASP.NET项目中cshtml突然出错,当前上下文中不存在名称“ViewBag” 原因:web.config 配置错误 这种情况是因为两个web.config文件版本不匹配,需要进行修 ...

  5. selenium2 WebDriver 在asp.net项目中的应用

    selenium2 WebDriver是一款跨平台的 自动化测试工具,它可以操纵浏览器,模拟用户行为,非常方便用户进行自动化测试. .net项目使用它,首先要通过 Visual Studio 的 nu ...

  6. 在ASP.NET项目中使用CKEditor +CKFinder实现图片上传功能

    前言 之前的项目中一直使用的是FCKeditor,昨天突然有个想法:为什么不试一下新的CKEditor呢?于是花了大半天的时间去学习它的用法,现在把我的学习过程与大家分享一下. 谈起FCKeditor ...

  7. ASP.NET项目中使用CKEditor +CKFinder 实现上传图片

    CKEditor是什么 CKEidtor是一个在线富文本编辑器,可以将让用户所见即所得的获得编辑在线文本,编辑器或自动将用户编辑的文字格式转换成html代码. 在ASP.NET工程中添加CKEdito ...

  8. 向asp.net项目中添加控件AspNetPager

    1.打开项目,把.dll文件放入项目中: 2.在工具栏中添加一个自定义选项卡

  9. 在ASP.NET项目中使用CKEditor

    CKEditor是什么 CKEidtor是一个在线富文本编辑器,可以将让用户所见即所得的获得编辑在线文本,编辑器或自动将用户编辑的文字格式转换成html代码. 在ASP.NET工程中添加CKEdito ...

随机推荐

  1. 阿里云ECS 实例Centos7系统磁盘扩容

    需求:一台阿里云的数据盘磁盘空间不足,需要扩容,我这里只有一个主分区,ext4文件系统. 因为磁盘扩容场景不同,阿里云的文档比较全面一些,所以先奉上阿里云的文档,下面开始我的操作步骤: 1.登录控制台 ...

  2. Qt类声明中Q_OBJECT的作用与报错解决

    2017-06-22 周四 大雨 北京 院里 新建作图类,继承自QCUstomPlot类 因为需要同时作8张图,都要单坐标缩放的功能,因此想干脆新建一个类,继承自QCUstomPlot,把需要的功能都 ...

  3. yum配置与使用

    yum的配置一般有两种方式,一种是直接配置/etc目录下的yum.conf文件,另外一种是在/etc/yum.repos.d目录下增加.repo文件. 一.yum的配置文件 [main] cached ...

  4. LeetCode~移除元素(简单)

    移除元素(简单) 1. 题目描述 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使 ...

  5. Vue源码之组件化/生命周期(个人向)

    大致流程 具体流程 组件化 (createComponent) 构造⼦类构造函数 const baseCtor = context.$options._base // plain options ob ...

  6. C++走向远洋——26(项目二,2,构造函数与析构函数)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:game.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  7. Java反射,泛型在Json中的运用

    最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的 ...

  8. C/C++ 存储类别

    table { margin: auto; } 本文介绍 C/C++ 中的存储类别.所谓的"存储类别"究竟是什么意思? 存储类别主要指在内存中存储数据的方式,其大致牵涉到变量的三个 ...

  9. 使用IDEA创建Maven整合SSM

    创建数据库 CREATE DATABASE `ssmbuild`; USE `ssmbuild`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ...

  10. sql数据库在登录异常时 ora-03114:未连接到ORACLE怎么办

    关闭SQL数据,重新启动,登录就好,不要用删除,或者其他方法,如果这麽做还是不可以,那么在想其他办法! 我自己就是这麽做的