热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于CORS,你了解的并不一定是对的?

CORS,有时候面试的也会问到。如果你和我一样,第一次遇到CORS(跨域资源共享),你想让服务器接收那些你拼接

CORS,有时候面试的也会问到。

如果你和我一样,第一次遇到 CORS (跨域资源共享),你想让服务器接收那些你拼接的 Ajax 请求并处理他们。所以你去 stackoverflow.com 复制一段代码来设置一些 HTTP Headers,让请求可以正常工作。

但是,可能还有一些事情你应该知道。

CORS 是什么,不是什么

新手通常混淆的原因,就是因为他们并不明确 CORS 能做什么。首先,CORS 并不是一种安全措施,实际上恰恰相反:CORS 是一种绕过“同源策略(SOP)”的方法。同源策略是一种安全措施,阻止您向其他域发出Ajax请求。

同源策略声明一个域上的网站,无法向另一个域发出 XMLHttpRequest(XHR) 请求。这可以防止恶意网站向已知网站(比如 Facebook 或者 Google)发出请求,改变用户的登录状态,以便可以冒充其他用户。此策略由浏览器实现(所有浏览器都实现了同源策略,尽管实现细节上存在细微的差别),这意味着此策略并不适用于从服务器,或者任何其他HTTP客户端(比如 cURL ,postman)发出的请求。此外,服务器同样无法完全控制它:服务器将处理每个请求,并假设他们都来自可信域,请求是否会被阻止完全取决于浏览器。

同源策略绝不意味着防止攻击者向您的服务器发出请求(因为攻击者显然不会使用浏览器)。它只是为了防止合法用户在使用知名浏览器浏览网站时,在不知情的情况下,向你的网站发起请求。

CORS 是一种绕过同源策略的方法,在某些情况下,您希望一些特殊的站点可以向你的服务器发起请求,即使正常情况下它会被阻止。(通常,是允许您的前端应用向您的API发出请求)。

CORS 是如何工作的

与HTTP的相同,CORS基本上也是浏览器和服务器之间的对话。假设你前端的域名为 domain-a.com ,后端API的域名为 domain-b.com,对话会是这样的:

  • 浏览器:“Hey domain-b.com,domain-a.com上的这个脚本要向你发起一次Ajax请求,但是我应该阻止它,除非你告诉我这个请求是没问题的。”

  • 服务器:“我不知道,但是我可以告诉你,https://domain-a.com 只允许发送 GET,POST,OPTIONS 和 DELETE 请求,并且需要每10分钟验证一次。”

浏览器想了想:“ yeah,这是个正确的域名,我应该给他发送请求。”

  • 浏览器:“Hey domain-b.com,我想在这个终端向你发送 POST 请求。”

  • 服务器:“没问题,这是你的 200 ”

或者,如果用户位于不同的域,则对话会更短:

  • 浏览器:“Hey domain-b.com,malicious-domain.com(恶意站点)上的这个脚本要向你发起一次Ajax请求,但是我应该阻止它,除非你告诉我这个请求是没问题的。”

  • 服务器:“我不知道,但是我可以告诉你,https://domain-a.com 只允许发送 GET,POST,OPTIONS 和 DELETE 请求,并且需要每10分钟验证一次。”

浏览器想了想:“ Oh,这不是正确的域名,我们最好不要发送请求”,然后在控制台打印了错误。
译注:第二种,使用开发者工具查看时,看不到 Response Headers,但是可以看到下图中的提示

在浏览器中的样子

在上面的小场景中,浏览器提出的第一个问题称为 预检请求,对应的 HTTP 谓词是 OPTIONS。遇到这种预检请求,服务器应该总是返回一个 200 的响应,没有正文,但是会包含 Access-Control-Allow-Origin ,以及一些其他响应头。在我们的示例中响应头如下:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://domain-a.comAccess-Control-Allow-Methods: GET, POST, OPTIONS, DELETE
Access-Control-Max-Age: 3600

它告诉浏览器,它只能响应来自 domain-a.com 的请求,可以处理 GET, POST, OPTIONS 或者 DELETE 请求(PUT 请求会被阻止),并且他可以缓存此信息 3600 秒,所以它不需要都发起一个新的 OPTIONS 请求。

当然,如果我们使用其他域名,这将不起作用。浏览器会发送 OPTIONS 请求,然后在控制台中抛出异常,并且永远不会发送 POST 请求。

很直接,对吧?

但是,也存在一些陷阱…

关于 CORS 的棘手问题

所求请求都包含 CORS 头(headers)

您可能会认为,如果您的服务器响应 OPTIONS 请求时返回 200,然后你将这些正确的响应头去掉。然后你将看到浏览器先发送了 OPTIONS 请求,然后发送了其他请求,其他请求挂掉了… 这是因为每个请求(GET, POST, 或者其他请求)都应该包含相同的响应头:“Access-Control-Allow-Headers”。

并非所有请求都会触发预检请求

有一些请求不会触发预检请求,比如 GET 请求,或者 Content-Type 设置为 application/x-www-form-urlencoded 的 POST 请求。这些是浏览器一直允许的“简单请求”,(即使在CORS不支持的情况下,你依然可以使用超链接(a标签)或者使用 POST 请求向其他网站提供表单,您可以在此处找到完整列表。在 POST 请求的情况下,结果会有些违反直觉:浏览器将发出 POST 请求(因此您的服务器可能会保留一些数据),然后忽略响应。

在传统的Web应用程序中,您可以使用 application/json 作为 content-type,因此会有预检请求,但请记住,您的服务器可能仍会收到来自其他域的 POST 请求,因此请不要盲目接受它们。

被允许的域名必须包含协议

您不能只将 mydomain.com 当做域名使用,它还需要包含协议,(例如:https://mydomain.com)。有趣的是,你不能同时接收 http 和 https ,因为……

您只能允许一个域

您可以使用 Access-Control-Allow-Origin: * 来允许每个域访问,也可以只允许一个域访问。这意味着如果您需要多个域来访问您的API时,您需要自己处理它。

处理此问题的最简单方法是在服务器上维护允许访问的域列表,如果域位于该列表中,则动态的改变响应头的内容。下面是一个 PHP 的例子:

$allowedDomains = ["http://www.mydomain.com","https://www.mydomain.com","http://www.myotherdomain.com","http://www.myotherdomain.com",];$originDomain = $_SERVER['HTTP_ORIGIN'];if (in_array($originDomain, $allowedDomains)) {header("Access-Control-Allow-Origin: $originDomain");};

或者 Node.js 的例子

app.use(function(req, res, next) {const allowedOrigins = ["http://www.mydomain.com","https://www.mydomain.com","http://www.myotherdomain.com","http://www.myotherdomain.com",];const origin = req.headers.origin;if(allowedOrigins.indexOf(origin) > -1){res.setHeader('Access-Control-Allow-Origin', origin);}return next();});

同源策略适用于 Chrome 和 Safari 的文件系统,不适用于 Firefox 的

如果您向本地文件发出请求,Firefox会认为它始终位于同一个域上并允许该请求。基于 Webkit 的浏览器(如Chrome或Safari)会将此视为安全风险,并阻止对本地文件的Ajax查询。解决这个问题的唯一方法是使用Firefox,或安装将发送 Access-Control-Allow-Origin: * 响应头的Web服务器。正如 @brianjenkins94 在评论中指出的那样,您也可以用 —disable-web-security 参数来启动Chrome 。

iOS WKWebview需要CORS

如果您正在开发使用 webview(使用Cordova或Ionic)的移动应用程序,Android将不会给您带来任何麻烦,但iOS上的新 WKWebview 将需要CORS。这意味着您几乎必须始终将 Access-Control-Allow-Origin 标头设置为 * ,但实际上这并不理想。

另一个选择是不在您的应用程序中发出Ajax请求并使用 cordova 插件来生成本机 http 请求,这将很方便的绕过同源策略。

谢谢阅读 !
如果您想要更深入地了解CORS,请访问MDN:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

关于本文
译者:@joking_zhang
译文:https://segmentfault.com/a/1190000019824580
作者:@Nicolas Bailly
原文:https://dev.to/nicolus/what-you-should-know-about-cors-48d6

最后

看完点个赞,分享一下吧,让更多的朋友能够看到。如果你喜欢前端开发博客的分享,就给公号标个星吧,这样就不会错过我的文章了。

好文和朋友一起看~


推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • Postgresql备份和恢复的方法及命令行操作步骤
    本文介绍了使用Postgresql进行备份和恢复的方法及命令行操作步骤。通过使用pg_dump命令进行备份,pg_restore命令进行恢复,并设置-h localhost选项,可以完成数据的备份和恢复操作。此外,本文还提供了参考链接以获取更多详细信息。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • ShiftLeft:将静态防护与运行时防护结合的持续性安全防护解决方案
    ShiftLeft公司是一家致力于将应用的静态防护和运行时防护与应用开发自动化工作流相结合以提升软件开发生命周期中的安全性的公司。传统的安全防护方式存在误报率高、人工成本高、耗时长等问题,而ShiftLeft提供的持续性安全防护解决方案能够解决这些问题。通过将下一代静态代码分析与应用开发自动化工作流中涉及的安全工具相结合,ShiftLeft帮助企业实现DevSecOps的安全部分,提供高效、准确的安全能力。 ... [详细]
  • 软件测试对于减少程序BUG有多大帮助?有经验的程序员通常认为测试与代码同等重要,测试可以减少代码变更或扩展的不确定性。测试应该方便阅读简单且能快速运行 ... [详细]
  • SAP接口编程PyRFC 调用 BAPI_FIXEDASSET_CREATE1创建固定资产
    本篇演示通过PyRFC调用BAPI_FIXEDASSET_CREATE1在SAP系统中创建固定资产,再一次体验一下Python与其它语言相比的简洁性。首先简单说明B ... [详细]
author-avatar
閆小恰
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有