cookie与CSRF攻击

2,704 阅读8分钟

CSRF攻击介绍

详细可看这篇文章:XSS攻击与CSRF攻击

HTTP cookies

cookie的作用域

domain和path定义了cookie的作用域:即允许cookie应该发给哪些URL。

Domain

domain指定了哪些主机可以接受cookie。如果没指定,默认为set-cookie的origin,不包含子域名。如果指定了domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如developer.mozilla.org)。

Path

Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。

例如,设置 Path=/docs,则以下地址都会匹配:

  • /docs
  • /docs/Web/
  • /docs/Web/HTTP

SameSite attribute

SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,(其中 Site (en-US) 由可注册域定义),从而可以阻止跨站请求伪造攻击(CSRF)。

SameSite cookies 是相对较新的一个字段,所有主流浏览器都已经得到支持。

SameSite 可以有下面三种值:

  • None。浏览器会在同站请求、跨站请求下继续发送 cookie,不区分大小写。(此时,secure属性必须设置为true,即请求必须要支持https)
  • Strict。浏览器将只在访问相同站点时发送 cookie。
  • Lax。与 Strict 类似,但用户从外部站点导航至URL时(例如通过链接)除外。

问题:

  1. chrome在80版本之后,更新了cookie的携带机制,把原来Cookie的SameSite属性默认值,由None改成了Lax,这就会导致一些需要第三方cookie的应用产生了异常。

sameSite跨站发送cookie.png

  1. 部分浏览器不能加SameSite=none,比如IOS 12的Safari,以及一些老版本的chrome浏览器,它们会错误的把SameSite=none识别成SameSite=strict

具体不兼容的浏览器可以见这里

概念介绍

首先我们要了解一些基本概念,有助于我们对整个流程的理解。MDN上介绍的很详细了,我就不赘述了。大家可点击链接仔细阅读:

浏览器的同源策略(Same-origin policy)

跨源资源访问

同源策略控制不同源之间的交互。这些交互通常分为三类:

  1. 跨域写操作一般是允许的。如链接,重定向和表单提交。特定少数的HTTP请求需要添加 preflight。
  2. 跨域资源嵌入一般是允许的。如:
  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。
  • <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的 HTTP 头部 Content-Type 。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
  • 通过 <img> 展示的图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...
  • 通过 <video> <audio> 播放的多媒体资源。
  • 通过 <object><embed><applet> 嵌入的插件。
  • 通过 @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
  • 通过 <iframe> 载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互。
  1. 跨域读操作一般是不被允许

跨域数据存储访问

访问存储在浏览器中的数据,如 localStorage 和 IndexedDB,是以源进行分割。每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对属于其它源的数据进行读写操作。

Cookies 使用不同的源定义方式。一个页面可以为本域和其父域设置 cookie,只要是父域不是公共后缀(public suffix)即可。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains) 访问 cookie。当你设置 cookie 时,你可以使用 DomainPathSecure、和 HttpOnly 标记来限定其可访问性。当你读取 cookie 时,你无法知道它是在哪里被设置的。 即使您只使用安全的 https 连接,您看到的任何 cookie 都有可能是使用不安全的连接进行设置的。

跨源资源共享(CORS)

内容安全策略(CSP)

内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS (en-US)) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

为使CSP可用, 你需要配置你的网络服务器返回 Content-Security-Policy HTTP头部 ( 有时你会看到一些关于X-Content-Security-Policy头部的提法, 那是旧版本,你无须再如此指定它)。如下图:

image.png

除此之外, <meta> 元素也可以被用来配置该策略, 例如

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

CSP 的 default-src详细解析可参考链接,以下是对图片中出现的数据的解析:

  • connect-src:用于控制允许通过脚本接口加载的链接地址
  • img-src:图片资源
  • script-src:JavaScript脚本资源
  • frame-src:<frame> <iframe>资源
  • font-src:字体资源
  • media-src:多媒体资源
  • style-src:css资源

'self'

指向与要保护的文件所在的源,包括相同的 URL scheme 与端口号。必须有单引号。一些浏览器会特意排除 blob 与 filesystem 。需要设定这两种内容类型的站点可以在 Data 属性中进行设定。

'unsafe-inline'

允许使用内联资源,例如内联 <script> 元素(javascript: URL)、内联事件处理器以及内联 <style> 元素。必须有单引号。

<scheme-source>

协议名如'http:' 或者 'https:'。必须带有冒号,不要有单引号。同时你还可以指定数据协议(data schema)(不推荐使用)。

  • data: 允许 data: URIs 作为内容的源。这是不安全的。攻击者可以注入任意 data: URI 。不要轻易使用这种形式的源,尤其是脚本,绝对不要使用。
  • mediastream: 允许 mediastream: URIs 作为内容的源。
  • blob: 允许 blob: URIs 作为内容的源。
  • filesystem: 允许 filesystem: URIs 作为内容的源。

另外,webpack也支持CSP内容安全策略的配置,感兴趣的可自己配置试一下。其中的nonce的解释如下:

  • 'nonce-<base64值>' 特定使用一次性加密内联脚本的白名单。服务器必须在每一次传输政策时生成唯一的一次性值。否则将存在绕过资源政策的可能。

易混淆概念区分

对于这些文章,有些疑惑的点,我在这里进行了补充。

image.png

问题一: 如上图,描述的SCRF攻击。cookie不是说不能跨域吗?为什么CSRF攻击中,用户打开一个第三方网站Web(B),发送请求,能够携带Web(A)的cookie。

cookie不能跨域访问(除非是在cookie的domain允许的子域),但是可以跨站点共享,这两个概念要区分开来。

在同一浏览器,当前打开的多个Tab页网站,无论是否为同一站点,cookie都是共享可见的。这个共享不是说每个网站的脚本可以访问别的网站的cookie,而是说,你向同一服务器发送请求时,会带上浏览器保存的对于那个服务器的所有cookie,而不管你从哪个网站发起的请求

以下,是实际举例说明:

  • 下图是当前用户登陆的网站【a】的cookie,且用户没有关闭网站【a】 截屏2021-05-04 下午5.31.43.png

  • 同时,用户打开一个新的tab标签【b】,请求网站【a】的用户登录信息。如下图可见,请求携带了网站【a】的cookie,并且接口正常返回了用户信息 截屏2021-05-04 下午5.31.43.png

  • 而,我在另一个浏览器,发起同样的请求,则没有返回用户信息,也没有携带cookie,因为cookie没有存在这个浏览器。

截屏2021-05-04 下午5.45.46.png 因此,cookie不能跨域访问,但cookie可以跨站点共享

问题二:为什么form表单提交没有跨域问题,但ajax提交有跨域问题?

浏览器的同源策略的本质:一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求。

form 提交(submit函数)之后,是不会有任何数据返回的。没机会读任何东西,所以可以认为是无害的,不在同源策略之内。

而 AJAX 是可以读取响应内容的,因此浏览器的同源策略不允许这样的行为。

所以同源策略会限制 Ajax读取响应内容 ,不会限制 Form发送请求。

  1. 对于跨域的AJAX请求,需要设置withCredentials = true,否则请求不会携带Cookie,同时,Access-Control-Allow-Origin不能设置为*,要设置成相应的域名
router.get('/api/data', (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', ctx.headers.origin);
  ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , myheader');
  ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
  ctx.set('Access-Control-Allow-Credentials', 'true');
};
  1. 对于非跨域的请求,或者跨域的非AJAX请求--比如通过form表单提交,浏览器会自动携带上Cookie。

如何防止CSRF攻击

  1. 现在浏览器默认的Samesite就是lax,但是在简单请求的情况下,浏览器任然会自动带上cookie到服务器,所以,这个时候还是需要加上服务器token验证才保险。(如果要设置cookie跨域传输,这里有些要注意的地方,见文章:chrome 默认 cookie 的 SameSite=Lax,导致 http 模式的站点的第三方 cookie 无法进行跨域传输。)
  2. 给cookie设置SameSite属性为strict,浏览器将只在访问相同站点时发送 cookie,但是这样就没办法设置cookie跨域传输了。
  3. 校验referer和origin,referer是否是从不安全的域名跳转过来,origin是否不是安全的
  4. 使用CSP,详细如上问解释