美文网首页php 相关
了解 CORS-跨站资源共享

了解 CORS-跨站资源共享

作者: michael_jia | 来源:发表于2016-07-24 23:58 被阅读441次
    缘起
    • 关于跨域问题,听得多,但一直未曾梳理。
    • 最近因为跨站 POST 无法上传图片,初识 cors,折腾多次;因认识不深而存在非认证即可上传安全漏洞;
    • 7月11-12日关于 ajax 处理 502 错误页面跳转问题(bug#692)又折腾了一次;
    • 7月19日又发现 IE8&9 存在兼容问题,因为 IE 是通过 XDomainRequest object 来支持 cors 的,幸好 MoonScript 帮助实现了在 ie8&9 下和其他浏览器一样进行跨站访问;但是不幸的是,不能携带 cookies,也就是说和认证有关的都不行;Eric Law 的文章 《XDomainRequest – Restrictions, Limitations and Workarounds》 揭示了这一点;
    • NoteCode 首先应用 cors,让我们解脱了 JSONP,同时在前后端代码上理清了很多问题。
    什么是 CORS

    假设有两个站点,A 站是一个应用站点,S 站是一个服务站点(比如 API 站点);A 站的 js(ajax) 访问 S 站提供的服务或者资源,这样的请求就是 cors 请求(Cross-Origin Resource Sharing 跨站资源共享)。
    请移步 HTTP access control (CORS) @ mozilla.org Use Cases and Design Decision FAQ @ w3 wiki 查看详细。

    Origin 请求头是 cors 请求的一个简单标识

    简单地说,cors 请求的一个明显标识就是在请求中含有 Origin: 请求头。
    Origin: 请求头和 Access-Control-Allow-Origin: 响应头组成了 cors 的一个最简单用法;

    • 实现了 CORS 协议的浏览器会自动发送 Origin: 请求头;
    • 实现了 CORS 协议的服务端如果允许 Origin: 指定的站点访问,则在回应请求头 Access-Control-Allow-Origin: 中包含这个站点即可;
    预请求(Preflight request)和 实际请求(Actual request)

    这是两类请求,一般简单请求通常并不需要事先搞一个 Preflight 请求,只有使用了特殊的 Content-Type、有非标准的或者自定义的 request header 时,浏览器才会在提起实际请求前首先发送 Preflight 请求。

    何时 preflight?
    满足以上所列条件的跨站点请求就是简单请求,不需要事先 preflight
    • preflight 以 OPTIONS 方法发起请求
      目的在于发起实际业务请求之前,向服务端事先了解对跨站点访问控制策略的实施情况;
    • preflight 请求头
      - Origin: 应用站点,类似:http://www.example.com
      - Access-Control-Request-Method: 列出实际请求希望使用的HTTP方法;
      - Access-Control-Request-Headers:
      Access-Control-Request-Headers: 列出自定义的 header;如果 Content-Type 值为自定义的,也需要将 Content-Type 列入;
    • preflight 请求头示例
    Origin: http://www.example.com
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: X-PINGOTHER, Content-Type
    
    • preflight 响应头示例
    Access-Control-Allow-Origin: http://www.example.com
    Access-Control-Allow-Methods: POST, GET, OPTIONS
    Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
    Access-Control-Max-Age: 86400 指明对于 OPTIONS 请求的缓存时长;
    
    实际请求(Actual Request)
    • 请求头
    Origin: http://www.example.com
    Cookie: ...
    

    Requests with credentials 时(XMLHttpRequest.withCredentials = true;),在请求头中会携带上 Cookie: 请求头;
    这对于提供服务的 S 站来说,是一个重要且简捷的地方,S 站可以管理自己域下的 Cookie,就可以避免在主域名下种 cookie 而导致的种种问题。
    应用站点和服务站点是同一个主域名时,这是一个简单做法。
    比如:WWW 应用站点收集用户登录信息(简单如用户名/密码),通过 AJAX 到 API 服务站点进行认证后访问服务站点的资源,后续所有 API 操作都需要带上 Cookie 等凭证信息

    • 响应头
    Access-Control-Allow-Origin: http://www.example.com
    Access-Control-Allow-Credentials: true
    
    • 错误提示


      假如在 Preflight 时,`Access-Control-Allow-Headers` 没有包含实际请求所提交的相关请求头,则会报告错误提示
    dropzone.js 的预请求问题
    dropzone 是一个支持拖拽的文件上传 js 库,比如图片上传
    同源策略(Same Origin Policy

    同源策略是客户端脚本(javascript)的重要安全度量标准。所有浏览器都遵循这个标准。例如:XMLHttpRequest 请求就只能访问源站的资源,也就是说 A 站的 js 只能访问 A 站的资源;如果访问其他网站资源,浏览器会拒绝将返回的内容传递给 js,目的在于避免 A 站的恶意脚本窃取 B 站页面的敏感信息;

    关于跨站请求的可能的误解

    浏览器并不会阻止 AJAX 发起跨站请求,浏览器也会正常接收服务端的返回内容,只不过为了履行安全策略,浏览器并不会向上层调用者返回 AJAX 响应内容,也就是说返回结果被浏览器拦截了;
    我们在处理 bug# 692 时就误以为 jQuery 有问题,Google 了半天,在 stackoverflow 上才有一个提示说可能是 CORS 问题;

    使用 curl 调试 cors 请求(OPTIONS 请求示例)
    curl -I --verbose -X OPTIONS \
    -H "Origin: http://www.example.com" \
    -H "Access-Control-Request-Method: POST" \
    -H "Access-Control-Request-Headers: X-Requested-With" \
    http://api.example.com/index.php?r=j
    
    服务端 PHP 代码示例
    if(isset($_SERVER['HTTP_ORIGIN']) && stripos($_SERVER['HTTP_ORIGIN'], APP_TOP_DOMAIN) !== false){
        header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
        header('Access-Control-Allow-Credentials: true');
    
        if($_SERVER['REQUEST_METHOD']=='OPTIONS'){
            header('Access-Control-Allow-Headers: Content-Type, Cache-Control, X-Requested-With');
            header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
            header('Access-Control-Max-Age: 86400');
        }
    }
    header('Access-Control-Expose-Headers: Date');
    
    关于 cors 的缓存
    Last-Modified:Wed, 17 Aug 2016 16:00:00 GMT
    Cache-Control:public, max-age=86400
    
    • Vary: Origin 问题


      未标识 `Vary: Origin` 导致的问题
    fonts 需要授权使用

    fonts 要么放到同一个域名下,要么放到 cdn,但 需要 enabling CORS
    nginx 片段示例:add_header Access-Control-Allow-Origin *

    参考
    bug# 692 备注
    • 前端 js 无法直接捕获 API 50X 错误;
      服务端接收到了 jQuery AJAX 请求,也正确地返回了,但是 jQuery 就是没有正确返回调用,本来以为这是 jQuery 问题,其实不是;
    • 前端 js 只好通过超时来推导服务端出现问题;
    • 超时发生时,直接跳到 502 页面;

    相关文章

      网友评论

        本文标题:了解 CORS-跨站资源共享

        本文链接:https://www.haomeiwen.com/subject/zftvjttx.html