---
title: SR-05 輸出與回應
eyebrow: Guidant AI 資安檢視總報告 · 安全需求（SR）
h1: SR-05 輸出與回應
subtitle: 出口跳脫、回應白名單、安全標頭、推出去的最小化
lede: 資料從我們這裡「出去」的每一個口——匯出檔、信件、日誌轉送、HTTP 回應、第三方頻道、外部 AI——都要在出口處理：內容依目的地格式跳脫、回應只給該給的欄位、瀏覽器端有兜底標頭、推出去的只說「有事發生」。這一章 **5 條**：2 條有掃描實例、3 條是標準做法。
chips:
  - { text: "5 條", kind: accent }
  - { text: "掃描驗證 2", kind: ok }
  - { text: "標準做法 3", kind: plain }
---

> 讀法見[總表](SR-00-overview.html)。每條六欄：要求／為什麼／適用連線／對應威脅／驗證方式／來源。現況不在這裡，看[符合性矩陣](SR-matrix.html)。

## 這一章管什麼

輸入章（[SR-04](SR-04-input-handling.html)）管「進來的東西不能信」，這一章管「出去的東西要處理」。同一段使用者填的文字，放進 Excel 是公式、放進 Word 範本是語法、放進 HTML 信件是網頁、放進目錄查詢是篩選條件、放進日誌是換行——**每個目的地的「特殊字元」不一樣，所以跳脫必須在出口、依目的地做，不能在入口做一次了事**。回應與標頭是另一側：瀏覽器收到什麼、第三方頻道收到什麼、外部 AI 收到什麼，都應該是「有清單可說明的最小集合」。

| # | 要求 | 來源 |
|---|---|---|
| SR-05.1 | 使用者輸入在出口依目的地格式跳脫 | 掃描驗證 |
| SR-05.2 | 回應欄位白名單；設定類回應遮密鑰；錯誤只回代碼 | 掃描驗證 |
| SR-05.3 | 前門對所有回應加安全標頭 | 標準做法 |
| SR-05.4 | 推到第三方頻道的內容最小化 | 標準做法 |
| SR-05.5 | 資料出境（外部 AI）的範圍有清單、客戶看得到 | 標準做法 |

---

## SR-05.1 使用者輸入在出口依目的地格式跳脫 {#sr-05-1}

| 欄位 | 內容 |
|---|---|
| **要求** | 任何使用者填的文字，在**離開系統的出口**必須依目的地格式跳脫，不在入口一次處理：Excel 匯出——以 `=`、`+`、`-`、`@` 開頭的儲存格前加 `'`；Word 範本——範本語法（`{{…}}`）轉義；HTML 信件——放進範本前一律 `html.escape()`；LDAP 查詢——`*`、括號、反斜線等篩選條件特殊字元不得原樣進 filter；Google 搜尋語法——先把反斜線換成兩個、再跳脫單引號（順序不能反），資料夾編號驗格式；Markdown 訊息（Telegram）——跳脫 Markdown 特殊字元，Discord 至少去掉 `@everyone`／`@here` 與連結語法；日誌轉送（syslog）——換行、歸位、定位、反斜線轉成看得見的轉義字元，在格式化器（出口）做，不在記錄點做。**內容一個位元都不得少**，只改呈現形式 |
| **為什麼** | 欄位裡的一段字在別人電腦、我們的伺服器或客戶的監控系統裡被當成指令執行：稽核人員開 Excel 時執行公式、系統以官方寄件身分發出釣魚信、帳號欄塞萬用字元以任一員工身分登入、往客戶的資安監控系統捏造稽核紀錄 |
| **適用連線** | [C02](../connections/C02-frontdoor-to-api.html) 前門 → api（匯出 Excel／Word）、[C10](../connections/C10-api-to-smtp.html) api → SMTP（HTML 信件）、[C11](../connections/C11-api-to-ldap.html) api → LDAP（查詢字串）、[C14](../connections/C14-api-to-google-drive.html) api → Google Drive（搜尋語法）、[C16](../connections/C16-api-to-chat-webhook.html) api → 聊天 webhook（Markdown 訊息）、[C12](../connections/C12-api-to-siem.html) api → SIEM（日誌控制字元） |
| **對應威脅** | C02 [T9 使用者的字變成程式：公式、範本語法、網頁、AI 指令](../connections/C02-frontdoor-to-api.html) 🟠、C10 [T4 用系統的寄件身分發釣魚信](../connections/C10-api-to-smtp.html) ⚪、C11 [T2 帳號欄塞萬用字元改寫查詢](../connections/C11-api-to-ldap.html) 🟠（無掃描實例）、C14 [T4 使用者的字拼進 Google 的搜尋語法](../connections/C14-api-to-google-drive.html) 🟡、C12 [T2 往客戶的 SIEM 塞假紀錄](../connections/C12-api-to-siem.html) 🟡、C16 [T2 用系統的名義在客戶頻道發釣魚訊息](../connections/C16-api-to-chat-webhook.html)（無掃描實例，同 C10 T4 的 M06-27） |
| **驗證方式** | 逐目的地各打一筆：暱稱填 `=cmd\|' /C calc'!A0` 後匯出任務 Excel，儲存格內容必須以 `'` 開頭、不得被 Excel 當公式；描述填 `{{ 1+1 }}` 後匯出 Word，文件內必須原樣顯示；暱稱填 `<a href=…>請重新登入</a>` 後觸發通知信，信件原始碼必須是 `&lt;a…`；LDAP 登入帳號填 `*`，必須登入失敗；專案名稱結尾帶 `\` 加 `'` 後重建雲端資料夾，搜尋條件不得被改寫；登入帳號欄填「名字＋換行＋假日誌」，SIEM 收到的必須是**一筆**含 `\n` 字樣的紀錄；Telegram 暱稱填 `[請重新登入](https://x)`，頻道裡必須看到原始字元而非連結 |
| **來源** | 掃描驗證：C02 D7（實例 [M21-1](../STRIDE/E-elevation-of-privilege.html#m21-1)、[M11-18](../STRIDE/T-tampering.html#m11-18)、[M11-19](../STRIDE/E-elevation-of-privilege.html#m11-19)、[M11-20](../STRIDE/E-elevation-of-privilege.html#m11-20)、[M06-26](../STRIDE/E-elevation-of-privilege.html#m06-26)）、C10 D4（實例 [M06-27](../STRIDE/S-spoofing.html#m06-27)）、C11 D2、C14 D4（實例 [M07-4](../STRIDE/I-information-disclosure.html#m07-4)、[M24-14](../STRIDE/I-information-disclosure.html#m24-14)）、C12 D2（實例 [M09-4](../STRIDE/R-repudiation.html#m09-4)）、C16 D2◇ |

## SR-05.2 回應欄位白名單；設定類回應遮密鑰；錯誤只回代碼 {#sr-05-2}

| 欄位 | 內容 |
|---|---|
| **要求** | 回應 schema **必須**採白名單：只回明列的欄位，不得「資料上有什麼就吐什麼」。設定類回應（儲存空間、郵件、AI 金鑰、整合憑證）密鑰欄位**一律遮蓋**，遮蓋依「欄位標記」而非人工維護的名單，新欄位預設視為機密、明確標為非機密才放行。錯誤訊息**只回錯誤代碼**，資料庫原始錯誤、解析器例外、伺服器暫存路徑只進日誌。失敗紀錄只記位址與錯誤類別，帶密碼的設定物件不得進任何日誌 |
| **為什麼** | 任何登入帳號打一支「查設定」端點就拿到儲存空間與郵件伺服器的帳號密碼明文；錯誤訊息把資料庫原始錯誤、內部路徑整段回給畫面；寄信失敗把整組設定連密碼寫進紀錄，看得到紀錄或診斷包的人搜尋就得到憑證 |
| **適用連線** | [C02](../connections/C02-frontdoor-to-api.html) 前門 → api（所有設定與錯誤回應）、[C10](../connections/C10-api-to-smtp.html) api → SMTP（失敗紀錄不帶密碼）；推及 [C15](../connections/C15-api-to-issue-tracker.html)、[C16](../connections/C16-api-to-chat-webhook.html)（兩者都有「憑證讀取遮罩」的對應防線，同一要求） |
| **對應威脅** | C02 [T8 回應帶太多：設定連密碼一起回、錯誤訊息吐內部細節](../connections/C02-frontdoor-to-api.html) 🟠、C10 [T3 寄信失敗把密碼寫進紀錄](../connections/C10-api-to-smtp.html) 🟡 |
| **驗證方式** | 以一般登入帳號（非管理員）打每一支設定讀取端點，回應中所有密鑰欄位必須為遮罩值、整份回應搜尋不到已知的測試密碼；對存著的設定新增一個新密鑰欄位但不登記，回應必須仍遮蓋；送壞資料（例如損毀的 PDF、超長設定碼）使其失敗，回應 body 只含錯誤代碼，不含 stack、路徑、SQL 片段；把郵件伺服器位址改成不可達並按「寄測試信」，日誌搜尋測試密碼必須零命中 |
| **來源** | 掃描驗證：C02 D6（實例 [M02-5](../STRIDE/I-information-disclosure.html#m02-5)、[M22-2](../STRIDE/I-information-disclosure.html#m22-2)、[M22-3](../STRIDE/I-information-disclosure.html#m22-3)、[M04-12](../STRIDE/I-information-disclosure.html#m04-12)、[M11-22](../STRIDE/I-information-disclosure.html#m11-22)）、C10 D3（實例 [M16-2](../STRIDE/I-information-disclosure.html#m16-2)） |

## SR-05.3 前門對所有回應加安全標頭 {#sr-05-3}

| 欄位 | 內容 |
|---|---|
| **要求** | 前門（nginx）**必須**對所有回應加上：`Content-Security-Policy`——腳本只准來自本站、禁止 inline script；`Strict-Transport-Security`（HSTS）——讓瀏覽器記住永遠走 https；`X-Frame-Options`／`frame-ancestors`——不讓本站被嵌進別人的頁面；`X-Content-Type-Options: nosniff`——瀏覽器不得自行猜內容型別。標頭在前門統一加，不靠各頁各回應自己加 |
| **為什麼** | 別人的程式在本站網域執行（上傳的網頁檔被預覽、回呼頁網址參數進頁面）時，沒有全站 CSP 就沒有第二道牆擋住；沒有 HSTS，第一次打 http 那一下可被攔截；沒有 frame 限制，本站可被嵌進惡意頁面做點擊劫持。這也是資安問卷必問項 |
| **適用連線** | [C01](../connections/C01-browser-to-frontdoor.html) 瀏覽器 → 前門（標頭在這裡加）；推及 [C02](../connections/C02-frontdoor-to-api.html)（所有 API 與預覽回應經前門送出）、[C04](../connections/C04-browser-to-api-drive-oauth.html)（OAuth 回呼頁，不需登入的伺服器渲染頁） |
| **對應威脅** | C01 [T3 不需登入的頁面把網址參數放進頁面程式](../connections/C01-browser-to-frontdoor.html) 🟠、C01 [T4 回程把別人上傳的內容當網頁交給瀏覽器執行](../connections/C01-browser-to-frontdoor.html) 🟠——這兩種手法已有掃描實例，但本條屬兜底：「個別修好後，標頭讓同類手法繞過第一道防線時仍被瀏覽器擋」，本身無掃描實例 |
| **驗證方式** | 對前門任一 URL（含 404 頁、API 回應、靜態檔）發 `curl -I`，回應必須同時帶四個標頭，CSP 不得含 `unsafe-inline` 與 `*` 來源；以 http 開 :80 必須只回 301；用 `<iframe src="https://本站">` 嵌入測試頁，瀏覽器必須拒絕載入；上傳 `.html` 改名 `.png` 後預覽，回應必須帶 `nosniff`。標頭覆蓋率用腳本對路由清單逐一檢查，不抽樣 |
| **來源** | 標準做法：C01 D5◇、C01 D6◇ |

## SR-05.4 推到第三方頻道的內容最小化 {#sr-05-4}

| 欄位 | 內容 |
|---|---|
| **要求** | 推到第三方頻道（Discord、Telegram、外部問題單）的內容**必須**最小化：只說「有事發生、請到系統查看」，**不得**帶客戶內部的專案名、人名、附件、截圖。使用者自填、可能夾帶客戶內部資料的內容（意見回饋、附件）送進外部系統前必須先提醒或過濾 |
| **為什麼** | 頻道與外部問題單是第三方雲端服務：推出去的內容就存在對方伺服器，客戶的專案名稱、人名、截圖含個資的附件一旦送出就無法收回，且合規問卷必問「資料是否流到第三方」 |
| **適用連線** | [C16](../connections/C16-api-to-chat-webhook.html) api → 聊天 webhook、[C15](../connections/C15-api-to-issue-tracker.html) api → 外部問題單 |
| **對應威脅** | 無掃描實例（標準做法）。相關手法：C16 [T3 webhook 網址外流後冒充系統發話](../connections/C16-api-to-chat-webhook.html)（無掃描實例）——頻道內容越少，網址外流時可被讀走的越少 |
| **驗證方式** | 觸發每一種會推到 Discord／Telegram 的事件（任務指派、專案啟動、批次完成），檢查實際送出的 HTTP body：必須不含測試專案名、測試人員暱稱與任何檔名；送出意見回饋含附件至外部問題單前，畫面必須有「內容會送到第三方」的提示，或附件被過濾 |
| **來源** | 標準做法：C16 D5◇、C15 D4◇ |

## SR-05.5 資料出境（外部 AI）的範圍有清單、客戶看得到 {#sr-05-5}

| 欄位 | 內容 |
|---|---|
| **要求** | 資料送到外部 AI 服務的範圍**必須**有清單：哪些功能會把哪類資料送到哪家 AI，寫在設定頁或文件，客戶看得到。送內文片段（而非整份）的功能，片段怎麼切、切多長必須說得出來並與實際行為一致 |
| **為什麼** | 客戶要簽核「資料是否跨境、是否由第三方處理」時，若答不出來送了什麼，就無法通過合規問卷，也無從判斷自家敏感內容是否外流；片段規則說不清楚，客戶無法評估單次最多送出多少 |
| **適用連線** | [C13](../connections/C13-api-to-external-ai.html) api → 外部 AI |
| **對應威脅** | 無掃描實例（標準做法）。相關手法：C13 [T4 AI 儀表板這條路不檢查權限，資料整包送出去](../connections/C13-api-to-external-ai.html) 🟡——該手法已有實例（無法納入本條來源），本條是「讓客戶有能力發現送出範圍」的透明面 |
| **驗證方式** | 逐功能（證據自動分類、AI 儀表板、聊天框）抓實際送往 AI 閘道的請求 body，欄位與長度必須落在清單所列範圍內；清單中的功能數與實際呼叫外部 AI 的程式進入點數一致（新增進入點時清單未更新要被 review 擋下）；設定頁或文件必須能指出「分類功能送的是內文片段，上限 N 字」 |
| **來源** | 標準做法：C13 D7◇ |

---

*來源：[連線頁](../connections/C01-browser-to-frontdoor.html)的防線（D）；去重對照在 `requirements/_drafts/D-to-SR.md`。*
