---
title: C01 瀏覽器 → 前門
eyebrow: Guidant AI 資安檢視總報告 · 連線清單
h1: C01 瀏覽器 → 前門
subtitle: guidant-fe，HTTPS :443
lede: 使用者的瀏覽器連到系統唯一對外入口的那條線。這一頁回答：這條線**怎麼連**、**哪些功能走它**、它會給系統帶來**什麼威脅、駭客怎麼打**、我們**要怎麼防、目前做到哪**。
chips:
  - { text: "必有", kind: plain }
  - { text: "不驗身分", kind: warn }
  - { text: "威脅 4 種", kind: accent }
  - { text: "掃描命中 5 條", kind: plain }
---

> **這一頁怎麼來的**：[DFD Level 0](../DFD/dfd-level0.html#c01) 把產品運作時的連線編成 C01～C25；[STRIDE 六頁](../STRIDE/S-spoofing.html)每一條問題都標了發生在哪幾條線。這裡以**連線**為單位，把這條線的特性、掃描在這條線上抓到的攻擊手法、以及由此該有的防線整理成一頁。個別問題修了沒不在這頁講，請點條目連回 STRIDE 頁。

## 一、連線圖

```{.mermaid cap="C01 — 瀏覽器到前門。前門是整套系統唯一對外開的埠，自己不驗身分，收到的流量分三路：靜態頁自己回、/api/1.0 轉給 api（C02）、/socket.io 轉給 socketio（C03）。紅色是不需登入就打得到的入口。"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB'}}}%%
flowchart LR
    U(["👤 使用者瀏覽器<br/>（網路上任何人）"])
    ATK(["🕵 攻擊者<br/>（不需帳號）"])
    subgraph MAIN["🏠 主系統 stack（只有 :443 對外）"]
        direction TB
        FE["guidant-fe<br/>nginx 前門 :443<br/>TLS 終止・分流・不驗身分"]
        API["guidant-api :8000<br/>驗 JWT"]
        SIO["guidant-socketio :8002<br/>驗 JWT"]
        STATIC[("前端靜態頁")]
        FE -- "/api/1.0 · C02" --> API
        FE -- "/socket.io · C03" --> SIO
        FE -- "其他路徑" --> STATIC
    end
    subgraph OPEN["不需登入就打得到的入口（在 api 內、驗身分之前或之外）"]
        direction TB
        O1["登入／忘記密碼"]
        O2["Drive 授權回呼頁"]
        O3["每個請求的前置處理<br/>（記日誌、遮密碼）"]
    end
    U == "C01 · HTTPS :443<br/>頁面＋API＋WebSocket" ==> FE
    ATK -. "C01 · 同一條線<br/>不需帳號" .-> FE
    API --- OPEN
    classDef ext fill:#FBFCFC,stroke:#9AA8AA,stroke-dasharray:3 2
    classDef open fill:#FDECEC,stroke:#C0392B
    class U,ATK ext
    class O1,O2,O3 open
```

## 二、這條線怎麼連

| 項目 | 現況 |
|---|---|
| **誰 → 誰** | 使用者的瀏覽器 → `guidant-fe` 容器（nginx）。這是整套系統**唯一對外開的埠**，後端 api（:8000）與 socketio（:8002）都不對外，全由這裡分流 |
| **協定／埠** | HTTPS :443（TLS 1.2／1.3）。:80 只做 301 轉到 https，**不供站** |
| **傳什麼** | 三種東西共用同一條線：① 前端靜態頁（Vue 打包檔）；② 業務 API 請求（路徑 `/api/1.0`，轉給 api，即 [C02](../DFD/dfd-level0.html#c02)）；③ 即時推播的 WebSocket（路徑 `/socket.io`，轉給 socketio，即 [C03](../DFD/dfd-level0.html#c03)） |
| **怎麼驗身分** | **這條線本身不驗**。身分（JWT）夾在請求裡往後送給 api 驗；前門只負責加密通道與分流。所以「不需要登入就打得到的東西」全在這條線上暴露給網路上任何人 |
| **憑證** | 客戶自備（正式 CA 或自簽），由 compose 唯讀掛進容器，**不進 image**。憑證檔缺失時 nginx 直接啟動失敗，不退回 http——刻意的，http 站台會「看得到畫面但登不進去」（瀏覽器在非 https 下不給 Web Crypto） |
| **反向代理帶的資訊** | 轉給後端時加 `X-Real-IP`／`X-Forwarded-For`／`X-Forwarded-Proto`，後端靠這些知道真正來源與 https |
| **大小與時間限制** | 上傳大小**不限**（`client_max_body_size 0`，交由後端各模組自己把關）；API 逾時 600 秒、WebSocket 3600 秒 |
| **何時存在** | 必有。沒有這條線就沒有產品 |

依據：FE repo `nginx.onprem.conf`（落地版正式出貨設定）、DFD Level 0 的 STG 盤點。

## 三、哪些功能會走這條線

**全部**——使用者在畫面上做的每件事都從這裡進來。但對這條線的威脅分析來說要分兩群，因為前門不驗身分：

| 群 | 功能 | 對威脅的意義 |
|---|---|---|
| **① 不需登入就打得到** | 登入頁、忘記密碼、Google Drive 授權回呼頁、所有靜態頁；以及**每一個請求在驗身分之前**系統先做的事（記操作日誌、遮密碼） | 網路上任何人都構成攻擊面，不需要帳號。這一群出的問題嚴重度天生高一級 |
| **② 登入後的回程** | 所有 API 的回應、檔案**預覽**（伺服器把儲存裡的檔交回瀏覽器畫出來）、匯出下載 | 回程把伺服器上的內容送進使用者瀏覽器、在**本站網域**裡執行。內容若是別人上傳的，就是把不可信的東西放進可信的網域 |

## 四、這條線會帶來什麼威脅、駭客怎麼打

掃描在這條線上抓到 5 條問題（[M13-1](../STRIDE/S-spoofing.html#m13-1)、[M04-15](../STRIDE/D-denial-of-service.html#m04-15)、[M24-1](../STRIDE/S-spoofing.html#m24-1)、[M02-4](../STRIDE/S-spoofing.html#m02-4)、[M03-13](../STRIDE/S-spoofing.html#m03-13)），歸納起來是**四種攻擊手法**。每種寫駭客實際的步驟與得手什麼，條目是實例。

| # | 風險 | 威脅 | 駭客怎麼打 | 得手什麼 | STRIDE | 實例 |
|---|---|---|---|---|---|---|
| T1 | 🔴 最嚴重 | **不需登入的入口把祕密從回程交出去** | ① 不需帳號，打開登入頁的「忘記密碼」；② 輸入目標的信箱（例如原廠管理員）送出；③ 系統把重設鑰匙直接寫在回應裡，不必收信；④ 拿鑰匙改掉目標密碼 | 接管任何知道信箱的帳號，含原廠最高權限管理員 | [S 冒充身分](../STRIDE/S-spoofing.html)、[E 權限提升](../STRIDE/E-elevation-of-privilege.html) | [M13-1 忘記密碼把重設鑰匙直接回傳給任何人](../STRIDE/S-spoofing.html#m13-1) 🔴 |
| T2 | 🟠 高 | **驗身分之前的處理被餵特製內容，把全站卡死** | ① 不需帳號，對任何網址（連不存在的都行）送一個請求，內容放 128KB 特製文字；② 系統在知道對方是誰之前就把整份內容拿去比對規則遮密碼；③ 比對卡住數秒到數分鐘；④ 同時送幾個，所有處理程序全卡住 | 不必任何帳號，讓整個產品對所有客戶停止回應 | [D 讓服務停擺](../STRIDE/D-denial-of-service.html) | [M04-15 不必登入，送一個特製內容的請求就讓整個產品停止回應](../STRIDE/D-denial-of-service.html#m04-15) 🟠 |
| T3 | 🟠 高 | **不需登入的頁面把網址參數放進頁面程式，在本站網域執行** | ① 不需帳號，做一個指向回呼頁的連結，參數裡塞 `</script><script>…`；② 寄給一個已登入的使用者；③ 對方一點，頁面被切開，攻擊者的程式在本站網域、以受害者的登入身分跑；④ 讀走登入權杖送出去 | 受害者的登入身分；受害者是管理員就是整家公司 | [S 冒充身分](../STRIDE/S-spoofing.html)、[E 權限提升](../STRIDE/E-elevation-of-privilege.html) | [M24-1 雲端硬碟授權回呼頁，點一個連結就被偷走登入身分](../STRIDE/S-spoofing.html#m24-1) 🟠 |
| T4 | 🟠 高 | **回程把別人上傳的內容當網頁交給瀏覽器執行** | ① 有帳號的人（或控制得了代理程式回報的人）上傳含程式的網頁檔，或把它改名成 `.png`；② 當證據或掃描報告存進系統；③ 稽核人員在證據清單點「預覽」；④ 伺服器照上傳者填的副檔名把它當網頁送回，瀏覽器在本站網域裡執行；⑤ 程式用受害者身分讀改資料 | 受害者（通常是稽核人員）的登入身分 | [S 冒充身分](../STRIDE/S-spoofing.html)、[E 權限提升](../STRIDE/E-elevation-of-privilege.html) | [M02-4 上傳的網頁檔，別人點預覽時被當成程式執行](../STRIDE/S-spoofing.html#m02-4) 🟠<br>[M03-13 掃描報告「檔名叫什麼就存什麼」，走同一條預覽路徑](../STRIDE/S-spoofing.html#m03-13) 🟠 |

**四種手法的共同點**：T1～T3 都利用「**前門不驗身分，驗身分之前與之外的東西對全世界開放**」；T4 利用「**回程落在本站網域，瀏覽器分不出內容是我們寫的還是別人上傳的**」。這兩個特性就是這條線的本質風險，不是某支程式的 bug——程式修好了，特性還在，下次加一個不需登入的端點或一種新的預覽格式，同樣的路就再開一次。

## 五、我們要怎麼防、目前做到哪

對應四種威脅與這條線的本質，防線分成七條。D1～D4 直接對應掃描抓到的四種手法；**標 ◇ 的（D5、D6 後半、D7）是依這條線的特性補的標準防線，掃描範圍沒涵蓋、沒有實例**。「目前」欄寫程式與部署裡**實際有的機制**；⚠️ 表示目前只靠慣例或只守到局部，沒有機制保證。

| # | 防線 | 擋哪種威脅 | 目前做到哪 |
|---|---|---|---|
| D1 | **不需登入的入口，回應不得帶任何可直接使用的祕密**（重設鑰匙、權杖、一次性碼）。祕密只能走第二條路（信箱）送到本人手上；成功與失敗的回應要長得一樣，不讓人探信箱存不存在 | T1 | ✅ 忘記密碼回應已改固定空白、成功失敗逐位元組相同。⚠️ 沒有統一的「不需登入端點回應檢查」，靠逐支 review |
| D2 | **驗身分之前只做便宜、有上限的事**。身分檢查之前執行的每一步（記日誌、遮密碼、解析內容）都要對輸入長度設上限、耗時隨長度線性 | T2 | ✅ 入口先把內容截到 64KB、遮密碼規則改線性、一個請求只遮一次，有效能測試守著。⚠️ 沒有規範要求新加的前置處理先過這條；`before_request` 裡其他步驟沒逐一盤過 |
| D3 | **不需登入的頁面，網址參數不得進頁面程式**。回呼頁、錯誤頁只認白名單代碼，顯示文字由前端查翻譯，不回顯外部字串；頁面帶 CSP 只准執行帶隨機碼的程式 | T3 | ✅ Drive 回呼頁已白名單＋跳脫＋CSP nonce。⚠️ 目前只有這一個不需登入的伺服器渲染頁；日後加其他 OAuth 回呼要照同一套 |
| D4 | **回程交回瀏覽器的檔案，由伺服器決定格式，不信上傳者說的**。可在瀏覽器裡打開的格式用白名單；伺服器讀檔頭驗真實格式；網頁類一律帶 CSP sandbox；所有檔案回應帶 `X-Content-Type-Options: nosniff`；檔名在進儲存前先消毒 | T4 | ✅ 預覽路徑全部做到（套件 `preview_policy.py`）；掃描報告檔名先過 `sanitize_report_filename()` |
| D5 ◇ | **全站 CSP 兜底**：前門對所有回應加 `Content-Security-Policy`，限制程式只能來自本站、禁 inline script，讓 T3、T4 這類「別人的程式在本站網域跑」即使繞過 D3／D4 也被瀏覽器擋 | T3、T4 | ⚠️ **沒有**。前門 nginx 沒設任何安全回應標頭；CSP 只在 Drive 回呼頁與預覽回應兩處局部存在 |
| D6 ◇ | **只走 HTTPS，憑證缺失寧可不啟動**；加 HSTS 讓瀏覽器記住永遠走 https，堵第一次打 http 那一下被攔的空窗；加 `X-Frame-Options`／`frame-ancestors` 不讓本站被嵌進別人的頁面（點擊劫持） | 這條線的前提——沒有加密通道，上面所有身分與資料都是明文 | ✅ :80 只 301、憑證缺失啟動失敗。⚠️ 沒有 HSTS、沒有 frame 限制；客戶用自簽憑證時瀏覽器警告會讓使用者習慣按「仍要前往」 |
| D7 ◇ | **請求大小與速率在前門就有上限**，不讓單一來源用大量或大體積請求耗盡後端 | T2 的延伸 | ⚠️ 前門 `client_max_body_size 0` 不限大小（刻意交後端把關），沒有速率限制。後端各模組各自有大小上限，但若某個新端點忘了設，前門不會幫擋 |

**最便宜的一步**：D5、D6 的安全回應標頭，在前門 nginx 加五行就有，一次覆蓋所有頁面與回應，也是資安問卷必問項。這一輪掃描 24 個模組頁沒有任何一條提到它，因為掃描範圍是程式碼不是部署設定。

## 六、依這些防線，掃描還沒看過的地方

- **不需登入端點的完整清單**（D1～D3）：目前知道的是登入、忘記密碼、Drive 回呼。應該用程式產一份「哪些路由不掛身分檢查」的清單逐一對照，而不是靠記憶。
- **驗身分之前的處理步驟清單**（D2）：攔截器裡目前做了哪幾件事、各自有沒有上限。這輪只修了遮密碼這一件。
- **前門安全回應標頭**（D5、D6）：一個都沒設，整條線最大的空缺。

---

*依據：STRIDE 六頁信任邊界連線標記（CM-2403／2404 驗收後版本）、FE repo `nginx.onprem.conf`、DFD Level 0、全 codebase grep 安全標頭（只有 `google_drive_integration_route.py` 與套件 `preview_policy.py` 兩處）。*
