什么是同源策略?

一、定义

同源策略(same origin policy)是netScape(网景)提出的一个安全策略,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。具体表现为浏览器在执行脚本前,会判断脚本是否与打开的网页是同源的,判断协议、域名、端口是否都相同,相同则表示同源。其中一项不相同就表示跨域访问。会在控制台报一个CORS异常,目的是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

浏览器采用同源策略,在没有明确授权的情况下,禁止页面加载或执行与自身不同源的任何脚本。

二、同源策略分类

  1. DOM 同源策略: 禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的(比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码)
  2. XMLHttpRequest 同源策略: 禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求(这一点里面其实包括了 ajax)。
  3. Cookie、LocalStorage、IndexedDB 等存储性内容同源策略: js中无法访问不属于同个源的cookie、LocalStorage中存储的内容。

具体来说,cookie和LocalStorage在控制哪些源可以访问的问题上还是细微的差别,父域在设置cookie的时候可以设定允许子域访问这段cookie,同时Cookie只和域名以及路径关联,如果是同个域名不同端口的源依然是共享同个域名下的Cookie的,而LocalStorage则是以源为单位进行管理,相互独立,不同源之间无法相互访问LocalStorage中的内容。

但是同源策略留了一些“后门”:

  1. 页面中的链接,重定向以及表单提交是不会受到同源策略限制的(未授权情况下,ajax 的表单提交是不被允许的,但是普通的表单是可以直接跨域的)。
  2. <script>、<img>、<link>这些包含 src 属性的标签可以加载跨域资源。但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

总结:

同源策略限制的内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM和JS对象无法获得
  • AJAX请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

三、同源策略的目的

简单的举两个例子

(1)如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

  1. 做一个假网站,里面用 iframe 嵌套一个银行网站mybank.com
  2. 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到mybank.com 的 dom 节点,就可以拿到用户的账户密码了。

(2)如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

  1. 用户登录了自己的银行页面mybank.com,银行页面向用户的cookie 中添加用户标识。
  2. 用户浏览了恶意页面evil.com,执行了页面中的恶意AJAX 请求代码。
  3. evil.com向mybank.com发起 Ajax HTTP 请求,浏览器会默认把mybank.com对应 cookie 也同时发送过去。
  4. 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了,而且由于 Ajax 在后台执行,用户无法感知这一过程。

注:

  • cookie 是浏览器根据你请求的页面进行发送的,而与从哪个页面发送过去请求无关。比如说,我访问了 a.com 以后获取了 a.com 的cookie ,然后我访问 q.com ,由于 cookie 域的限制,浏览器在请求 q.com 的时候不会携带 a.com 的cookie ,但是当 q.com 有一个向 a.com 发起的请求的时候,浏览器就会自动的在这个请求中带上 a.com 的 cookie。
  • 跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?
因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

跨域访问限制的作用流程大致如下:

  1. 浏览器发送跨域请求
  2. 接收response数据
  3. 检查响应头
    (1)如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
    (2)如果响应头中有允许跨域访问的设置,正常加载数据

即: 同源策略并不是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是请求正常发出去了,后端也正常相应了,只不过数据到了浏览器后浏览器不去作用加载而是丢弃了。

小结:
同源策略控制不同源之间的交互,这些交互通常分为三类:
(1)跨域读: 同源策略是不允许这种事情发生的,如果可以你就能使用 js 读取嵌入在 iframe 中的页面的 dom 元素,获取敏感信息了

(2)跨域写: 同源策略不阻止这种操作,比如向不同源的地址发送 POST 请求等,但是这种允许只限制在普通表单(而且是在没有 CSRF token 或者验证 referer 的情况下),对于 ajax 这种方式也是默认不允许的,如果随随便便允许就会出现使用 ajax 来进行 CSRF 请求的情况了

(3)跨域嵌入: 这种方式是默认允许的,我们可以在一个源中通过 iframe 嵌入 另一个源的页面,但是如果想限制这种操作的话,我们可以设置 x-frame-options 这个头,这样设置了这个头的页面就允许被嵌入到不同源的页面中了


跨站请求伪造-CSRF

跨站请求伪造概述

  • “假传圣旨”
  • “Client-Side”
  • hacker 用户可以伪造admin 用户的转账请求,强制admin 用户,转账给任意用户。

CSRF原理

基本概念

跨站请求伪造(Cross Site Request Forgery, CSRF),是一种攻击,它强制浏览器客户端用户在当前对其进行身份验证后的Web 应用程序 上执行非本意操作的攻击,攻击的重点在于更改状态的请求,而不是盗取数据,因为攻击者无法查看伪造请求的响应。

借助于社工的一些帮助,例如,通过电子邮件或聊天发送链接,攻击者可以诱骗用户执行攻击者选择的操作。如果受害者是普通用户,则成功的 CSRF 攻击可以强制用户执行更改状态的请求,例如转移资金、修改密码等操作。如果受害者是管理账户,CSRF 攻击会危及整个Web 应用程序。

关键点

  • 受害者没有退出登录,受害者保持身份认证。
  • CSRF 继承了受害者的身份和特权,代表受害者执行非本意的、恶意的操作。
  • CSRF 会借用浏览器中与站点关联的所有身份凭据,例如用户的会话Cookie,IP 地址,Windows 域凭据等。

目标

CSRF 的目标是更改用户账户的状态,攻击者利用CSRF 发送的请求都是更改状态的请求,比如,转账、更改密码,购买商品等等。

攻击过程

  1. 用户输入账号信息请求登录A网站。
  2. A网站验证用户信息,通过验证后返回给用户一个cookie
  3. 在未退出网站A之前,在同一浏览器中请求了黑客构造的恶意网站B
  4. B网站收到用户请求后返回攻击性代码,构造访问A网站的语句
  5. 浏览器收到攻击性代码后,在用户不知情的情况下携带cookie信息请求了A网站。此时A网站不知道这是由B发起的。那么这时黑客就可以进行一下骚操作了!

两个条件:

  • a 用户访问站点A并产生了cookie

  • b 用户没有退出A同时访问了B


关于Maven pom.xml中build下的resources标签

resources标签作用:描述工程中资源的位置

示例代码:

1
2
3
4
5
6
7
8
9
10
11
<resource> 
<targetPath>META-INF/plexus</targetPath>
<filtering>false</filtering>
<directory>${basedir}/src/main/plexus</directory>
<includes>
<include>configuration.xml</include>
</includes>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</resource>
  • targetPath: 指定build资源到哪个目录,默认是base directory

  • filtering: 指定是否将filter文件(即build下的filters里定义的*.property文件)的变量值在这个resource文件有效,例如上面就指定那些变量值在configuration文件无效。

  • directory: 指定属性文件的目录,build的过程需要找到它,并且将其放到targetPath下,默认的directory是${basedir}/src/main/resources

  • includes: 指定包含文件的patterns,符合样式并且在directory目录下的文件将会包含进project的资源文件。

  • excludes: 指定不包含在内的patterns,如果inclues与excludes有冲突,那么excludes胜利,那些符合冲突的样式的文件是不会包含进来的。

  • testResources: 这个模块包含测试资源元素,其内容定义与resources类似,不同的一点是默认的测试资源路径是${basedir}/src/test/resources,测试资源是不部署的。

注:默认情况下,如果没有指定resources,目前认为自动会将classpath下的src/main/java下的.class文件和src/main/resources下的.xml文件放到target里头的classes文件夹下的package下的文件夹里。如果设定了resources,那么默认的就会失效,就会以指定的includes和excludes为准