---
# System prepended metadata

title: 跨來源資源共用 CORS (Cross-Origin Resource Sharing）
tags: [Internet 網路知識與安全]

---

###### tags: `Internet 網路知識與安全`
# 跨來源資源共用 CORS (Cross-Origin Resource Sharing）

跨域問題主要是 `Server` 不同意 `Client` 存取改寫資料，要讓 `Server` 同意只能靠後端來解決。
:::spoiler
是一種使用額外 [HTTP](https://developer.mozilla.org/zh-TW/docs/Glossary/HTTP) 標頭令目前瀏覽網站的[使用者代理 (en-US)](https://developer.mozilla.org/en-US/docs/Glossary/User_agent "Currently only available in English (US)")取得存取其他來源（網域）伺服器特定資源權限的機制基於安全性考量，程式碼所發出的跨來源 HTTP 請求會受到限制。例如，[`XMLHttpRequest`](https://developer.mozilla.org/zh-TW/docs/Web/API/XMLHttpRequest) 及 [`Fetch`](https://developer.mozilla.org/zh-TW/docs/Web/API/Fetch_API "Fetch") 都遵守[同源政策（same-origin policy）]
:::
~~
> JSONP/常用的內容傳遞網路（content delivery networks, CDN）也是跨域的一種

---

## 同源政策 (Same-origin policy)
> 限制了程式碼和不同網域資源間的互動，自己的網站不能給別人存取和修改，目的是基於網路安全考量，防止被攻擊。

如何分辨是否同源 :
兩個來源 ( Origin ) 的  ==<font class="red">scheme、domain、port</font>== 三者相同，就會視為同源。
> Origin = scheme + domain + port

* **scheme**（通訊協定，即為 http、https）。
* **domain**（域名，例如 google.com）。
* **port**（埠號，例如 :8000）。

以下與 ==<font class="red">https://example.com:8000</font>==  不同來源 :
:::info
http://example.com  // 不同源，scheme 不同
https://example.org // 不同源，domain 不同
http://example.com:3000  // 不同源，port 不同
:::

---

## 簡單請求 VS 非簡單請求
當 `Client` 與 `Server` 溝通時，需要以 `Request` 來取得 `Server` 回傳的 `Response`，其中請求的動作會再細分為兩種 :
### 1. **簡單請求（simple requests）**
僅允許下列 <font class="red">HTTP</font> 方法：
* **GET** 
* **HEAD**
* **POST**

僅允許以下欄位標頭值：
* **Accept**
* **Accept-Language**
* **Content-Language**
* **Last-Event-ID**
==<font class="red">屬於 Content-Type 欄位</font> :==
* **application/x-www-form-urlencoded** (檔案內容)
* **multipart/form-data** (檔案類型)
* **text/plain** (纯文本格式)
> 請求不會觸發 CORS 預檢，就會被稱為所謂的所謂的「簡單請求」。
### 2. **非簡單請求 or 預檢請求（preflight request）**
請求為下列 <font class="red">HTTP</font> 方法其中之一：
* **PUT** 
* **DELETE**
* **CONNECT**
* **OPTIONS** 
* **TRACE**
* **PATCH**

為以下 <font class="red">Content-Type</font> 以外的標頭值：
* **application/x-www-form-urlencoded**
* **multipart/form-data**
* **text/plain**
> 發送請求時，瀏覽器發現是非簡單請求，因此先發出一個預檢請求 Options 方法來判斷請求是否安全，當伺服器端允許後，才能正式發送一開始的 Request。

---


## 解決辦法

三個「治標不治本」的方法：
* **關掉瀏覽器的安全性設置**
跨來源被擋住主要是因為瀏覽器的限制，因此可以透過關閉 `Google Chrome` 的安全性設置。
* **把 fetch mode 設成 no-cors**
在使用 `Fetch API` 時，出現跨域錯誤會提示使用 `no-cors` ，但是這個方法只是讓它不會報錯，依然拿不到 `Response` 。
* **不要用 AJAX 拿資料**
使用引入檔案的方式來拿資料，但是 2022 了，不會有人這樣使用，而且不方便。

:+1: 正確解決辦法  :

:::success
**後端加上**
**Access-Control-Allow-Origin**
Access-Control-Allow-Origin : `http://localhost:8081` (允許單個來源)
Access-Control-Allow-Origin :`*` (允許全部來源)
**Access-Control-Allow-Headers**
Access-Control-Allow-Headers : `Content-Type` (解析Content-Type類型檔案)
Access-Control-Allow-Headers : `*` (解析全部類型檔案)
**Access-Control-Allow-Methods**
Access-Control-Allow-Methods : `PUT` (允許單個方法)
Access-Control-Allow-Methods : `*` (允許全部方法)

**#需要帶上Cookie的情況**
**Access-Control-Allow-Credentials : true**
**Access-Control-Allow-Origin : http://localhost:8080** 
`帶上 Cookie 後，需給指定的來源，否則任何來源都能發出 Request 請求且帶上使用者的Cookie，會有安全性問題產生`

**#存取自訂 Header**
**Access-Control-Expose-Headers: X-List-Version**
`Header 為自定義時`

**#快取 preflight request**
**Access-Control-Max-Age: 300** `快取300秒`
`瀏覽器重複發出預檢，避免發生這種耗效能的情況，因此300秒內不會打到後端做預檢，而是直接沿用快取的資料`
:::

:::info
**Proxy Server (反向代理伺服器) :**
透過反向代理與客戶端溝通。但通常都是在工作上遇到此問題，因此不需要那麼費工費成本。
:::





<font class="red"></font>

<style>
.red {
  color: #CE0000;
}
</style>





