本文 AI 產出,尚未審核

ExpressJS (TypeScript) – CORS 與安全性設定

主題:Helmet 基礎安全防護


簡介

在 Web 應用程式的開發過程中,安全性往往是最容易被忽略卻又最關鍵的環節。即使功能完整、效能優秀,若缺乏基本的防護機制,仍可能因 XSS、點擊劫持、MIME sniffing 等常見攻擊而遭受資安風險。
Node.js 生態系統提供了 helmet 這個輕量級的中介軟體套件,專門為 Express(或 Nest、Koa 等框架)自動設定一系列 HTTP Header,以降低上述威脅的可能性。

ExpressJS + TypeScript 的專案中,正確引入與配置 helmet,不僅能提升應用的安全基礎,還能與 TypeScript 的型別系統結合,讓開發者在編譯階段即捕捉錯誤、避免錯誤設定。本文將從概念說明、實作範例、常見陷阱到最佳實踐,完整呈現如何在 Express + TypeScript 專案中使用 Helmet 進行基礎安全防護。


核心概念

1. Helmet 是什麼?

helmet 是一組 Express 中介軟體(middleware),它會在回應(response)中加入或調整 HTTP Header,以防止常見的 Web 攻擊。
主要包含以下幾個子套件(可單獨開關):

Header 功能說明 常見攻擊類型
Content-Security-Policy (CSP) 限制瀏覽器載入資源的來源 XSS、資料注入
X-Frame-Options 防止頁面被嵌入 <iframe> 點擊劫持 (Clickjacking)
X-Content-Type-Options 禁止 MIME sniffing MIME sniffing 攻擊
Strict-Transport-Security (HSTS) 強制使用 HTTPS 中間人攻擊 (MITM)
Referrer-Policy 控制 Referer 資訊的傳遞 隱私洩漏
X-XSS-Protection 啟用瀏覽器內建 XSS 防護 (已被部分瀏覽器棄用) XSS

重點:Helmet 只是一層防護,仍需配合其他安全措施(如驗證、授權、輸入驗證)才能構築完整的防線。

2. 為什麼要在 TypeScript 中使用 Helmet?

  • 型別安全@types/helmet 提供完整的型別定義,開發時能即時得到 IDE 提示與編譯錯誤,避免錯誤的選項名稱或設定值。
  • 模組化設定:透過 TypeScript 的介面(interface)可以自訂 helmet 的選項,讓設定更具可讀性與維護性。
  • 與其他中介軟體共存:在 TypeScript 中使用 app.use() 時,編譯器會檢查傳入的中介軟體是否符合 RequestHandler 型別,減少因參數錯誤導致的執行時例外。

3. 安裝與基本使用

npm i helmet               # 安裝核心套件
npm i -D @types/helmet     # 安裝型別定義(開發環境)

src/app.ts(或 src/server.ts)中加入:

import express, { Request, Response, NextFunction } from 'express';
import helmet from 'helmet';

const app = express();

// 1️⃣ 套用 Helmet 完整預設
app.use(helmet());

// 2️⃣ 若想自訂個別 Header,可使用物件參數
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", 'https://cdnjs.cloudflare.com'],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:'],
      },
    },
    // 其他子套件可在此關閉或調整
    frameguard: { action: 'deny' },
    hsts: { maxAge: 31536000, includeSubDomains: true },
  })
);

// 測試路由
app.get('/', (req: Request, res: Response) => {
  res.send('Hello, Helmet with TypeScript!');
});

export default app;

說明:上述程式碼先以 app.use(helmet()) 套用 全部預設,接著再傳入自訂設定,會 覆寫 預設行為。若只想啟用部分功能,可分別引用 helmet.xxx()


程式碼範例

以下提供 5 個常見且實用的 Helmet 設定範例,每個範例皆附上說明與在 TypeScript 中的型別寫法。

範例 1️⃣:最小化 CSP(Content‑Security‑Policy)

import helmet from 'helmet';

// 只允許同源(self)與 CDN 之 script
app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", 'https://cdn.jsdelivr.net'],
      styleSrc: ["'self'", "'unsafe-inline'"], // 若使用 inline style 必須允許
      imgSrc: ["'self'", 'data:'],
    },
  })
);

為什麼需要 CSP?
CSP 能告訴瀏覽器「只能從哪裡載入資源」,即使攻擊者成功注入惡意 <script>,瀏覽器仍會因違反政策而拋棄。


範例 2️⃣:防止點擊劫持(X‑Frame‑Options)

app.use(
  helmet.frameguard({
    action: 'sameorigin', // 只允許相同網域嵌入
    // action: 'deny'   // 完全禁止嵌入
  })
);

說明sameorigin 允許自己的頁面在 <iframe> 中顯示,適合多層子系統;若不需要任何嵌入,使用 deny 更安全。


範例 3️⃣:啟用 HSTS(Strict‑Transport‑Security)

app.use(
  helmet.hsts({
    maxAge: 60 * 60 * 24 * 365, // 1 年(秒)
    includeSubDomains: true,
    preload: true, // 若想提交至 Chrome HSTS preload list
  })
);

注意:啟用 HSTS 後,所有 HTTP 請求將自動升級為 HTTPS。在開發環境(如 localhost)不建議開啟,可利用 process.env.NODE_ENV 判斷。


範例 4️⃣:禁用 MIME Sniffing(X‑Content‑Type‑Options)

app.use(helmet.noSniff()); // 會在回應加入 X-Content-Type-Options: nosniff

說明:此 Header 告訴瀏覽器不要自行猜測檔案類型,避免惡意腳本被偽裝成圖片或 CSS 之類的檔案。


範例 5️⃣:自訂 Referrer‑Policy

app.use(
  helmet.referrerPolicy({
    policy: 'no-referrer-when-downgrade', // 預設值,可根據需求調整
    // 常見選項:'no-referrer'、'origin'、'strict-origin-when-cross-origin'
  })
);

為什麼要設定 Referrer‑Policy?
它能控制瀏覽器在發送請求時,是否攜帶前一頁的 URL。若放出太多資訊,可能洩漏內部路徑或機密參數。


常見陷阱與最佳實踐

陷阱 可能的後果 解決方式
在開發環境直接套用 HSTS 一旦設定 maxAge 大於 0,瀏覽器會將 localhost 記錄為 HTTPS,之後必須手動清除 HSTS 記錄才能回到 HTTP。 使用 if (process.env.NODE_ENV === 'production') 包裹 helmet.hsts(),或在本機設定 maxAge: 0
CSP 設定過於寬鬆(如 script-src * CSP 失去防護意義,仍會受到 XSS 攻擊。 僅允許可信來源,盡量避免 unsafe-inlineunsafe-eval
忘記在 express.json() 前使用 Helmet 若先解析 JSON,再套用 Helmet,有可能因錯誤的 Content-Type 產生安全漏洞。 建議 app.use(helmet()) app.use(express.json())
同時使用多個相同功能的中介軟體(例如自行設定 X-Frame-Options 產生 Header 重複或衝突,導致瀏覽器忽略或回傳錯誤。 讓 Helmet 統一管理相關 Header,除非確實需要覆寫,否則不要自行再設定。
忘記在測試環境關閉 CSP 的 reportOnly 開發時因 CSP 阻擋資源而無法正常測試。 可使用 helmet.contentSecurityPolicy({ reportOnly: true }) 先觀察報告,再正式啟用。

最佳實踐

  1. 分環境設定:在 src/config/security.ts 中統一管理 Helmet 設定,依 process.env.NODE_ENV 切換不同的值。
  2. 結合 express-rate-limit:防止暴力破解與 DoS 攻擊,兩者相輔相成。
  3. 使用 helmet.hidePoweredBy():隱藏框架資訊(X-Powered-By: Express),降低資訊洩漏。
  4. 定期審查 CSP 報告:使用 report-urireport-to 讓瀏覽器把違規請求回報給你的端點,持續優化政策。

實際應用場景

場景 1:企業內部 API 服務

公司內部的微服務需要 嚴格的 HTTPSHSTS,避免員工在不安全的 Wi‑Fi 上被中間人攻擊。

if (process.env.NODE_ENV === 'production') {
  app.use(
    helmet({
      hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
      noSniff: true,
      referrerPolicy: { policy: 'no-referrer' },
    })
  );
}

同時結合 express-rate-limit 限制每分鐘請求上限,提升整體安全性。

場景 2:公開的前端單頁應用(SPA)

SPA 需要載入外部 CDN(如 Bootstrap、Font Awesome),但不想讓任意第三方腳本執行。

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", 'https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com'],
      styleSrc: ["'self'", 'https://fonts.googleapis.com', "'unsafe-inline'"],
      fontSrc: ["'self'", 'https://fonts.gstatic.com'],
      imgSrc: ["'self'", 'data:'],
    },
    reportOnly: false,
  })
);

若 CSP 阻擋了合法資源,可先開啟 reportOnly 觀察報告,再正式啟用。

場景 3:多租戶 SaaS 平台

平台允許客戶自訂子域名(customer1.example.com),必須 限制子域名間的資源共享

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", (req) => `'https://${req.hostname}'`],
        // 每個租戶只能載入自己子域名的腳本
      },
    },
    frameguard: { action: 'sameorigin' }, // 不允許跨租戶嵌入
  })
);

透過函式型 scriptSrc,依請求的 hostname 動態產生允許的來源。


總結

  • Helmet 是在 Express(含 TypeScript)環境中快速提升 HTTP 層安全的首選工具。
  • 透過 型別定義,我們可以在編譯階段即捕捉錯誤,確保設定正確且不會因拼寫錯誤產生意外。
  • CSP、HSTS、X‑Frame‑Options、X‑Content‑Type‑Options、Referrer‑Policy 等子套件,各自針對不同攻擊向量提供防護,建議在正式環境一次全部啟用,開發環境則依需求調整。
  • 分環境管理結合速率限制定期檢視 CSP 報告 是落實安全的關鍵步驟。

只要在專案的入口檔(app.ts / server.ts)中加入適當的 helmet 設定,您就已經為 Express 應用鋪設了一層堅實的防護牆。未來隨著需求變化,只需要微調 helmet 的選項或加入自訂 Header,即可持續維護安全水準。祝您開發順利,應用安全無虞!