APIM CORS digiRunner

前端開發遭遇 CORS 問題與解決方案

陳瑞泰 John Chen 2025/06/08 23:27:57
131

# 前端開發遭遇 CORS 問題與解決方案

## 問題:前端框架開發與 CORS 的衝突

前端框架在開發時,經常會遭遇 CORS(跨來源資源共享)問題,這主要因為:
- 前端工程師在 IDE 上開發
- API server 是另一台伺服器
- 導致了跨域請求問題
- 在正式部署時基於資安稽核的規範,通常不允許使用 CORS 的 header

## 開發環境解決方案

### 1. 使用代理伺服器

- 在本地開發環境中設置代理伺服器,將 API 請求轉發到目標伺服器
- 常見前端框架如 React、Vue、Angular 都提供開發伺服器代理配置

### 2. 模擬 API 回應

- 使用 Mock Server 如 Mirage JS、Mock Service Worker (MSW) 等
- 在開發階段模擬 API 回應,減少對實際 API 伺服器的依賴

## 生產環境解決方案

### 1. 整合部署

- 將前端應用與 API 部署在相同域名下,避免跨域問題
- 例如:前端放在 `/` 路徑,API 放在 `/api` 路徑

http://localhost/**
http://localhost/api/**

### 2. 使用 API 閘道或反向代理

- 透過 Nginx、Apache 等反向代理伺服器
- 使用 AWS API Gateway、Azure API Management 等雲端服務
- 這樣前端請求會先到反向代理,然後由代理轉發到 API 伺服器

http_request -> proxy -> /**
http_request -> proxy -> /api/**

### 3. 微服務架構調整

- 設計邊緣服務 (Edge Service) 或 BFF (Backend For Frontend) 模式
- 為特定前端應用提供專用的 API 閘道

Nginx、F5、HA proxy、CLB、digiRunner...etc

### 實例:使用 Nginx 反向代理配置

server {
    listen 80;
    server_name your-production-domain.com;

    # 前端靜態文件
    location / {
        root /var/www/html;
        try_files $uri $uri/ /index.html;
    }

    # API 請求代理
    location /api/ {
        proxy_pass http://your-api-server:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

## CORS 與資安疑慮

### 過於寬鬆的 CORS 政策風險

- 設定 `Access-Control-Allow-Origin: *` 允許任何網站存取您的 API
- 這可能導致未授權網站執行跨站請求,增加 CSRF (跨站請求偽造) 攻擊風險
- 攻擊者可能從其控制的網站向您的 API 發送請求,利用使用者已建立的身份驗證

### 資訊洩露風險

- 寬鬆的 CORS 設定可能導致敏感資料被不受信任的網站存取
- 若允許憑證共享 (`Access-Control-Allow-Credentials: true`),未經授權的網站可能獲取敏感資訊

### 授權檢查繞過

- 不正確的 CORS 設定可能導致授權檢查被繞過
- 攻擊者可能利用特定 CORS 漏洞來執行未經授權的操作

## 合理使用 CORS 的建議

若必須使用 CORS,以下是降低風險的做法:

### 嚴格限制來源

- 明確指定允許的來源網域,避免使用萬用字元 `*`
- 例如:`Access-Control-Allow-Origin: https://trusted-site.com`

### 考慮使用動態 CORS 回應

- 根據請求來源動態生成 CORS 回應
- 僅允許已知且受信任的網域

### 謹慎處理憑證

- 僅在必要時啟用 `Access-Control-Allow-Credentials: true`
- 同時必須確保 `Access-Control-Allow-Origin` 不為 `*`

### 限制允許的 HTTP 方法與標頭

- 僅開放必要的 HTTP 方法
- 明確指定允許的請求標頭

### 實作額外的安全措施

- 使用 CSRF Token
- 實施內容安全政策 (CSP)
- 使用適當的身份驗證與授權機制

### 可能使用的情境

- 環境中沒有 LB 或 APIM 作為身份驗證站台
- 各端點自行驗證
- 如下圖說明:
`app.example.com` 將登入後的 token 傳送給 `api.othersite.com` 並要求它接受與驗證,`othersite` 在後台中新增白名單 ->`example`,確保它為信任的網站。
image

## VS Code 整合開發環境配置方案

### 1. 使用 Vite 開發服務器

// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' // 或其他框架插件

export default defineConfig({
  plugins: [react()],
  server: {
    port: 4200,
    proxy: {
      // 將 /api 開頭的請求代理到實際的 API 伺服器
      '/api': {
        target: 'http://your-actual-api-server:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

### 2. 使用 Angular CLI 內建代理

proxy.conf.json:

{
  "/api": {
    "target": "http://your-actual-api-server:8080",
    "secure": false,
    "pathRewrite": {
      "^/api": ""
    },
    "changeOrigin": true,
    "logLevel": "debug"
  }
}

angular.json 配置片段:

{
  "projects": {
    "your-app": {
      "architect": {
        "serve": {
          "options": {
            "browserTarget": "your-app:build",
            "proxyConfig": "proxy.conf.json",
            "port": 4200
          }
        }
      }
    }
  }
}

### 3. 使用 Express.js 作為開發服務器

// server.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const path = require('path');

const app = express();
const PORT = 4200;

// 靜態文件服務 - 前端部分
app.use('/web', express.static(path.join(__dirname, 'dist')));

// API 代理 - 後端部分
app.use('/api', createProxyMiddleware({
  target: 'http://your-actual-api-server:8080',
  changeOrigin: true,
  pathRewrite: {
    '^/api': '' // 移除 /api 前綴
  }
}));

// 捕獲所有其他請求並返回前端入口
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

app.listen(PORT, () => {
  console.log(`開發伺服器啟動於 http://localhost:${PORT}`);
});

### 4. 建立 VS Code 任務整合

.vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "啟動開發服務器",
      "type": "shell",
      "command": "node server.js",
      "isBackground": true,
      "problemMatcher": [
        {
          "pattern": [
            {
              "regexp": ".",
              "file": 1,
              "location": 2,
              "message": 3
            }
          ],
          "background": {
            "activeOnStart": true,
            "beginsPattern": "開發伺服器啟動於",
            "endsPattern": "開發伺服器啟動於"
          }
        }
      ],
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

### 5. 使用 webpack-dev-server

// webpack.config.js
const path = require('path');

module.exports = {
  // 其他 webpack 配置...
  
  devServer: {
    port: 4200,
    static: {
      directory: path.join(__dirname, 'dist'),
    },
    proxy: {
      '/api': {
        target: 'http://your-actual-api-server:8080',
        pathRewrite: { '^/api': '' },
        changeOrigin: true,
      },
    },
    historyApiFallback: {
      rewrites: [
        { from: /^\/web\/.*/, to: '/index.html' },
      ],
    },
  },
};

## 停用 Chrome 安全性檢查方案

一種不需修改前端程式碼的方法是使用特殊參數啟動 Chrome 瀏覽器,繞過 CORS 限制:

"C:\Program Files\Google\Chrome\Application\chrome.exe" --user-data-dir="C:/Chrome dev CORS" --disable-web-security

### 這種方法的優點

1. 無需修改程式碼 - 不需要在前端程式中加入任何代理配置或額外程式碼
2. 簡單直接 - 只需一個指令即可解決開發中的 CORS 問題
3. 適用於所有前端框架 - 不依賴於特定的前端技術或框架
4. 快速測試 - 可以快速測試連接到實際 API 的情況

### 這種方法的限制和注意事項

1. 僅限於開發環境 - 這種方式只適合開發環境使用,絕不能在生產環境中使用
2. 安全風險 - 停用安全性功能會使瀏覽器失去多種安全保護機制
3. 獨立瀏覽器實例 - 需要使用單獨的 Chrome 實例,與正常瀏覽分開
4. 不便於團隊協作 - 每位開發者都需要設置這個特殊的啟動方式
5. 與生產環境不一致 - 可能導致一些在生產環境中才會出現的問題被忽略

### 其他平台的啟動方式

- Mac OS:

open -n -a "Google Chrome" --args --user-data-dir="/tmp/chrome-dev-cors" --disable-web-security

- Linux:

google-chrome --user-data-dir="/tmp/chrome-dev-cors" --disable-web-security

## 案例分享:使用 API gateway或反向代理(digiRunner)

這是一個公開在 AWS 的開發網站, 我們按出它的 F12 來查看前端與後端的 URL 配置,下圖中的您可以看到 前端 的開頭 URL 都是:

/website/esg

image

前端 靜態網頁的設定在 digiRunner 中的靜態網頁反向代理配置:
image

我們再來看看 後端 的 F12 URL 配置:

/esg/api

image

後端 API 在 digiRunner中的註冊API配置, 設定完成後在API List 中盤點出所有的 API 如下:
image

整體來看 GreenSwift 網站的前後端 URL 配置如下:

http://localhost/website/esg/**
http://localhost/esg/api/**

## 案例分享: 教材來自 Youbute

### 【Nginx】【核心技术篇】27 基本使用 动静分离的原理与使用场景

image

【Nginx】【核心技术篇】27 基本使用 动静分离的原理与使用场景 - YouTube

### 【Nginx】【核心技术篇】28 基本使用 动静分离配置

image

【Nginx】【核心技术篇】28 基本使用 动静分离配置 - YouTube

## 總結

1. 開發環境:可以使用代理伺服器、模擬 API 回應、整合到 VS Code 或使用特殊啟動的 Chrome 來解決 CORS 問題

2. 生產環境:應使用反向代理、API 閘道或微服務架構調整來避免 CORS 問題,而不是直接開放 CORS

3. 資安考量:直接使用 CORS 存在資安風險,尤其是設定過於寬鬆時

4. 最佳實踐:在開發階段盡量模擬生產環境的請求結構,以確保程式碼順利部署

 

## 原始來源

https://hackmd.io/@PlxyJDuRSLKQ8WkiZzngmg/SyaNku41ex

 

陳瑞泰 John Chen
8B2A28082C7FD04466E455BF309D27D6
2025/06/10 11:47:42

英国威馬 https://www.tw9g.com/goods/pro7.html