同源策略和跨域资源共享

  在同一个浏览器中,可以同时打开一个或多个网站,也正是由于在同一个浏览器下,这些网站有着相同的上下文,比如Cookie,这些网站能同时读取到浏览器中的Cookie。由于Cookie通常携带用户身份信息,如果两个网站都能读取对方的Cookie,那就不安全了。比如 A 网站是一家银行,用户登录以后,A 网站在用户的机器上设置了一个 Cookie,此时用户去访问 B 网站,B 网站可以读取 A 网站的 Cookie,由于Cookie 保存了用户的登录状态,B网站就可以冒充用户,为所欲为。每个网站只能读取它自己的Cookie,这就同源策略。只要在同一个浏览器下,每一个网站发送的请求,都有着相同的上下文,所以需要同源策略。还有一种情况,那就是JS脚本的执行。如果B网站的JS就可以操纵银行网站,比如篡改页面DOM结构;篡改DOM元素的属性和值等,那也是危险的。

  同源策略,就是规定或限制从一个源加载的文档或脚本如何与另一个源的资源进行交互。怎么才算同源?一个URL有三部分组成,协议,域名和端口,只有这三个完全相同的URL,才能称为同源。要注意的是路径不算在内,即使后面的路径不同,也是同源。http://www.example.com/dir/index.html 和 http://www.example.com/dir2/other.html是同源的,即使dir和dir2不一致。具体来说,同源策略,分为两个方面

  可用性

    1,Html的作者决定跨域请求是否对本点站点安全。<script>, <img>, <link>等带有src属性可以跨域访问。

    2,允许跨域写操作,比如表单提交或者重定向请求,但这也会带来CSRF问题

  安全性:如果有A 和B两个站点,它们不同源,浏览器就要访止站点A的脚本向站点B发起危险动作,以下三种行为受到限制:

    (1)无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。

    (2) 无法接触非同源网页的 DOM。

    (3) 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。

  有了同源策略会怎样?

  浏览器在执行一个js脚本(或其他脚本)前,需要对这个脚本进行同源检测,如果加载这个脚本的页面和当前页面不同源,浏览器将拒绝执行此脚本;注意,浏览器并不关心js脚本来自何方(不关心js脚本从哪个域名、哪个”源”加载),它只关心加载脚本的那个页面是否和当前页面同源;可以这样认为,只要是页面加载的脚本,都和页面同源,无论这个脚本来自哪个”源”(source);

  但有的时候,确实需要跨域。比如,在web开发的过程,需要该问另外一个域,那就要用到CORS(跨域资源共享)。如果站点A允许B站点的脚本访问其资源,必须在HTTP响应中显示的告诉浏览器:站点B是被允许的。 具体来说,1,访问站点A的请求,浏览器应告知该请求来自站点B,2,站点A的响应中,应明确哪些跨域请求是被允许的。

  跨域请求又分为两种请求,简单请求和非简单请求。 只要满足以下条件的就是简单请求,

    1,GET、HEAD、POST三种方法之一    

    2,HTTP的头信息不超出几种字段: Accept,Accept-Language,Content-Language,Content-Type

    3,Content-Type的值只有三种:text/plain,multipart/form-data,application/x-www-form-urlencoded

  其余全是非简单请求。浏览器在发送请求之前,先判断是简单请求或非简单请求。如果是简单请求,浏览器直接发出CORS请求。过程如下:

    1,浏览器在请求头信息之中,增加一个Origin字段。Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)

    2,服务器做出响应,如果Origin指定的源,不在许可范围内,它会返回一个正常的HTTP回应,但头信息没有包含Access-Control-Allow-Origin字段。如果Origin指定的域名在许可范围内,服务器返回的响应,会携带Access-Control-Allow-Origin:头信息字段,比如 Access-Control-Allow-Origin: *。

    3,浏览器根据响应头部,进行判断,选择放行或报错。如果浏览器发现,响应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。如果浏览器发现,响应的头信息包含Access-Control-Allow-Origin字段,包含请求的域,就表示服务器允许跨域。

  通过上面,可以发现,跨域访问是在浏览器中控制的,服务器不做任何控制。只要浏览器发送请求,服务器就会响应,不管跨不跨域, 浏览器一定会收到响应。

  如果是非简单请求,浏览器会用OPTIONS方法,发送一个预检请求,以获知服务器是否允许该实际请求。预检请求中同时携带下面两个首部字段,服务器据此决定,该实际请求是否被允许。 

    Access-Control-Request-Method:告诉服务器,实际请求 使用什么方法:post、get 等。

    Access-Control-Request-Headers:告诉服务器,将实际请求所携带的首部字段。

  服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应 

    Access-Control-Allow-Origin:用于响应预检请求,表示允许该资源的外域 URI 

    Access-Control-Allow-Methods:用于响应预检请求,指明实际请求所允许使用的 HTTP 方法

    Access-Control-Allow-Headers:用于响应预检请求,指明实际请求中允许携带的 Header

    Access-Control-Max-Age: 86400  Access-Control-Max-Age表明该响应的有效时间为86400秒,也就是24小时;在有效时间内,浏览器无需为同一请求再次发送预检请求

   Access-Control-Allow-Credentials: 表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。CORS请求默认不发送Cookie和HTTP认证信息。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。当然,仅服务器同意,还不行,开发者必须在AJAX请求中打开withCredentials属性,比如xhr.withCredentials = true; 否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
  一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

  如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。

  

 

posted @ 2022-05-28 14:03  SamWeb  阅读(92)  评论(0编辑  收藏  举报