在最近的前后端分离项目开发过程中,频频遇到了CORS请求的问题。趁着思路还清晰,在这里做个备忘笔记。
本文部分内容参考自以下来源,在此深表感谢:
MDN官方文档 - 跨源资源共享(CORS)
跨域资源共享 CORS 详解 - 阮一峰的网络日志
什么是CORS?
根据MDN给出的定义:
跨源资源共享 (CORS) (或通俗地译为跨域资源共享)是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有HTTP方法和真实请求中会用到的头。
举例来讲,假设前端页面运行在http://localhost:8080上,当用户访问前端资源,例如http://localhost:8080/index.html, http://localhost:8080/1.html 等,对这些资源的请求都不会涉及跨域问题。而当浏览器需要访问运行在http://localhost:8081上的后端资源时,因为端口号并不相同,浏览器便会执行跨域请求。

前段定义中涉及到了一个关键的概念:Origin,也就是我们常说的域,或者源。我们可以将所有的HTTP请求归类为同源请求和跨域请求。我们通过以下几条来判断一个请求是否跨域:
- 协议类型(HTTP/HTTPS)
- 域名
- 端口号
只有当前页面和被请求资源的以上三条都一致的时候才是同源请求,否则一律按跨域请求处理。这里需要注意的是,域名匹配时不会进行DNS转换,也就是说http://localhost:8080和http://127.0.0.1:8080同样不在一个域内。
CORS请求
根据被发送请求的具体内容不同,我们可以将HTTP请求分为简单请求和非简单请求。在处理简单请求时,浏览器不需要发送预检(Preflight)请求,而在处理复杂请求时,浏览器会首先向服务端发送OPTION预检请求来获取服务器是否允许以CORS方式访问资源。有关预检,我们将在稍后具体介绍。
简单请求
当HTTP请求方式为以下之一:
- GET
- HEAD
- POST
且HTTP请求首部仅包括以下字段:
且Content-Type字段取(application/x-www-form-urlencoded
、multipart/form-data
、text/plain
)三者之一时,该请求才可被视作简单请求。
请求和响应报文
# 请求报文
GET /resources/public-data/ HTTP/1.1
Host: bar.other
Accept-Language: en-us,en;q=0.5
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
# 响应报文
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: application/xml
请求报文中的Origin
以及相应报文的Access-Control-Allow-Origin
共同构成了最基本的首部字段。Origin字段表明请求的来源,服务器返回的Access-Control-Allow-Origin指定了服务器接受的跨域访问源。*则表示接受所有外部跨域访问。
预检请求
预检请求需要在实际请求发送前向服务端发送OPTIONS请求来获取服务端是否支持CORS,支持的Origin,支持什么请求以及支持什么header。
请求和响应首部
# 请求首部
# 实际请求方法
Access-Control-Request-Method: POST
# 实际请求包括的headers
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
# 响应首部
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
# 请求有效期:在有效期内,浏览器无需重复发送OPTIONS预检请求
Access-Control-Max-Age: 86400
XSS&CSRF攻击
XSS攻击
XSS(Cross-site-Script跨站脚本攻击),通常是带有页面可解析内容的数据未经处理直接插入到页面上解析造成的。XSS根据攻击脚本的引入位置来区分为存储型XSS、反射型XSS以及MXSS(也叫DOM XSS)三种。
存储型XSS
多见于博客、论坛等允许用户输入内容的页面。xss注入脚本会被持久化到数据库中。举最简单的例子,在后端程序没有对用户输入进行任何处理的情况下,用户输入:
<script>alert("This is low level")</script>
当浏览器接受后端传回的数据并解析到<script>
标签时,便会加载这段代码实现攻击。
反射型XSS
多见于攻击者直接提供URL给用户,引导用户点击并进行攻击。例如:
https://malicious.link/q="%2F><script>alert%28document.cookie%29</script>
如果网页中的HTML是用到了query的参数,便会解析以上攻击代码,进而暴露用户的cookies。
常见的防御方法有:将诸如<>的特殊字符替换为HTMl转义符号,或通过正则表达式匹配括号内容并进行替换。
CSRF攻击:冒充用户之手
CSRF的全称是Cross Site Request Forgey。简单可以理解为攻击者盗用了用户的身份,以用户的名义发送恶意请求。
