跨來源資源共享(CORS)完整指南:打破瀏覽器的安全邊界
更新日期: 2025 年 3 月 4 日
本文為 資安入門 系列文,第 12 篇
- OWASP Top 10:最新的 Web 安全風險與防範措施(2021 版)
- MD5 加密演算法是什麼?
- SHA-1 是什麼?為什麼不再安全?
- 深入解析 Session:從概念到運作方式
- 什麼是 Session Fixation(會話固定攻擊)?
- HTTP 與 HTTPS 的差別:新手完整指南
- 跨站請求偽造(CSRF)入門指南:攻擊原理、實例與防禦方法
- 跨站指令碼攻擊(XSS)入門指南:從原理到防禦的全解析
- SQL 注入攻擊全解析:從入門到防禦實戰指南
- 新手指南:深入了解 Content-Security-Policy (CSP) 與網站安全
- 從零理解 Same-Origin Policy:瀏覽器安全的第一道防線
- 跨來源資源共享(CORS)完整指南:打破瀏覽器的安全邊界 👈所在位置
在現代網頁開發中,跨網站數據請求是一個常見的需求。
例如,你可能需要從第三方 API 獲取天氣資訊,或讓你的前端應用與不同的後端伺服器進行通訊。
然而,當你在 JavaScript 中發送這類請求時,可能會遇到瀏覽器的「CORS 錯誤」,導致請求被阻擋。
這是因為同源政策(Same-Origin Policy, SOP)會限制不同來源(Origin)的資源互動,以保護使用者的資訊安全。
但在某些合法情境下,我們仍然需要進行跨來源請求,這時候就需要「跨來源資源共享(CORS, Cross-Origin Resource Sharing)」機制來允許受控的跨域請求。
本文將帶你深入了解 CORS,包括它的基本概念、運作原理、如何設定,以及如何解決常見的 CORS 問題。
為什麼需要 CORS?
同源政策的限制
同源政策(SOP)是瀏覽器的核心安全機制之一,它禁止以下跨來源行為:
- 存取非同源 API 回應(例如
fetch('https://api.othersite.com')
) - 發送帶有 Cookie 的跨來源請求(例如需要用戶身份驗證的請求)
- 使用非標準 HTTP 方法(如 PUT、DELETE)與自訂標頭
如果沒有這些限制,惡意網站就可能竊取用戶的 Cookie、發送未授權的請求,甚至冒充用戶執行危險操作。
經典開發困境:
// 前端嘗試呼叫 API
fetch('https://api.weather.com/data')
.then(response => response.json())
.catch(error => console.error('CORS 錯誤:', error));
瀏覽器預設會封鎖這種請求,除非伺服器明確允許跨來源存取。
CORS 的歷史定位
在 CORS 之前,開發者主要使用 JSONP(JSON with Padding) 來繞過同源政策,但 JSONP 只能用於 GET
請求,且存在安全漏洞。
因此,CORS 成為了官方推薦的跨域解決方案。
時期 | 解決方案 | 缺點 |
---|---|---|
1995-2004 | JSONP | 只能用於 GET,存在安全漏洞 |
2005-現在 | CORS | 需伺服器配合設定 |
CORS 運作原理
CORS 的核心機制
當 JavaScript 在網頁中發送跨來源請求時,瀏覽器會在請求的 HTTP 標頭中加入 Origin
,表示該請求來自哪個來源(如 https://your-site.com
)。
伺服器則需在回應中包含適當的 CORS 標頭來允許該請求。
請求與回應流程:
如果 Access-Control-Allow-Origin
的值與 Origin
相符,則瀏覽器允許請求成功返回;否則,瀏覽器會阻擋請求並拋出 CORS 錯誤。
兩種請求模式
簡單請求(Simple Request)
當請求符合以下條件時,瀏覽器會直接發送請求,而不進行額外的安全檢查:
- 請求方法為 GET、HEAD 或 POST
- Content-Type 限制為
text/plain
、application/x-www-form-urlencoded
或multipart/form-data
- 請求不包含自訂標頭
請求範例:
GET /data HTTP/1.1
Origin: https://your-site.com
伺服器回應範例:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://your-site.com
預檢請求(Preflight Request)
當請求不符合簡單請求的條件(例如使用 PUT
、DELETE
、自訂標頭等),瀏覽器會先發送一個 OPTIONS
預檢請求,確認伺服器是否允許該請求,然後才發送真正的請求。
預檢請求範例:
OPTIONS /update-data HTTP/1.1
Origin: https://your-site.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-API-Key
伺服器回應範例:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: PUT, DELETE
Access-Control-Allow-Headers: X-API-Key
如何設定 CORS?
伺服器端設定
Express 設定 CORS
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://your-client.com',
methods: ['GET', 'POST', 'PUT'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
app.get('/data', (req, res) => {
res.json({ message: 'CORS 已啟用!' });
});
Nginx 設定 CORS
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://your-client.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}
常見 CORS 錯誤與解決方案
典型錯誤訊息
Access to fetch at 'https://api.example.com' from origin 'https://your-site.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
除錯檢查清單
- 確認伺服器是否返回 CORS 標頭
- 檢查
Access-Control-Allow-Origin
是否允許請求來源 - 是否發送了需要預檢的請求(如帶有
Authorization
標頭) - 如果帶有 Cookie,確保
credentials: 'include'
已啟用
fetch('https://api.example.com', {
credentials: 'include' // 傳送 Cookie
});
CORS 的安全性考量
危險設定:開放所有來源
app.use(cors({ origin: '*' })); // 可能導致安全風險
更安全的作法:白名單
const whitelist = ['https://trusted-site.com', 'http://localhost:3000'];
app.use(cors({
origin: (origin, callback) => {
if (whitelist.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('CORS 政策禁止此來源'));
}
}
}));
結論:掌握 CORS,安全開發無阻
CORS 不是錯誤,而是瀏覽器的安全防護機制。
理解其原理後,你可以:
✅ 正確配置伺服器 CORS 設定
✅ 快速診斷跨域錯誤
✅ 在安全前提下整合不同來源的 API
下次遇到 CORS 錯誤時,記得它就像國際護照查驗——只要擁有正確的「簽證文件」(HTTP 標頭),你的請求就能安全暢行!🚀