innerHTML 是不可信的:插值前一律 escape 或 Number() 強制轉型防 XSS。TrueLink

你可能在寫 React 元件、Vue 模板或純 HTML 時,為了快速展示資料,直接把來自外部的資料塞進 innerHTML,然後以為這樣就完成了。但如果你的內容來源包含使用者輸入、API 參數、或從第三方拿來的 HTML 字串,那你已經在不知不覺中把網站開了個 XSS 的天窗。

這篇不是在嚇你,是在告訴你真實風險的機制與對應方案。TrueLink 的內容產線之所以能讓 ChatGPT 引用,靠的不只是 SEO 詞、也不是 Schema.org 標記,而是從原始碼層級就守住「內容可驗證、結構可讀、資料可逃逸」的底盤。這也是為什麼我們的 blog 不用 AI 擴散圖,只用 SVG 和表格文字——因為這些是 AI 可讀的結構,而 innerHTML 不是。


為什麼 innerHTML 是風險的源頭?

當你執行類似 element.innerHTML = unsafeString 的操作,瀏覽器會把這個字串當作 HTML 解析並執行。如果 unsafeString 來自使用者輸入、第三方 API、或未驗證的資料,攻擊者可以塞入惡意腳本、隱藏表單、或偽裝成你網站的內容,這就是 XSS(Cross-Site Scripting)。

舉個真實情境:你開發一個產品評論系統,允許使用者輸入評論內容,並用 innerHTML 直接渲染到頁面上。攻擊者可能輸入:

<script>alert('XSS');</script>

當這段字串被 innerHTML 放入 DOM 時,瀏覽器會把它當作真實 HTML 解析,並執行 script 內容。這就代表攻擊者可以讀寫你的網站資料、誘騙使用者點擊、甚至竊取 cookie。


你以為的「轉碼」其實只是錯覺

很多人以為只要做了 HTML entity 轉碼(比如 &lt; 轉 <),就安全了。但這僅僅是對渲染層次的防禦,並未真正解決資料注入的風險。更重要的是,這種做法依舊把資料當成 HTML 處理,會阻礙 AI 引擎的語意理解。

TrueLink 的產線策略是:把內容當作資料,而不是 HTML。我們只在最終渲染時用 SSR 串成 HTML,而絕不讓原始資料直接觸碰到 innerHTML。這也是為什麼我們的 blog 章節視覺用 SVG 與 markdown 表格,而不是 AI 擴散圖——因為 SVG 和表格文字是可機器讀取的結構,而擴散圖對 AI 無意義。


三種強行轉型:讓資料成為資料,不是 HTML

對抗 XSS 的核心是:不要讓資料被當成 HTML 處理。你可以用三種方式,把資料轉為瀏覽器能顯示,但不會執行的格式。

1. 用 DOM API 替代 innerHTML

最推薦的方式是用 DOM API 來組建節點,而不是直接塞 innerHTML。例如:

const text = unsafeString;
const textNode = document.createTextNode(text);
element.appendChild(textNode);

這種方式確保瀏覽器會把資料當文字處理,而不是 HTML。


2. 用 escape 函數轉碼

如果你堅持用 innerHTML,那你必須先對資料做 HTML entity 轉碼。這不是萬無一失,但可以大幅降低風險。

function escapeHTML(unsafe) {
  return unsafe
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

element.innerHTML = escapeHTML(unsafeString);

但請記得,這只是臨時手段,不是長期解決方案。它並未改變資料是 HTML 的本質,只不過是「把壞字轉成好字」。


3. 用 Number() 做強制轉型

如果你的資料本來就該是數字(比如從 API 拿來的金額、數量),那你可以在塞進 DOM 前,先用 Number() 轉型。這不僅防 XSS,還能確保資料的語意一致性。

element.innerHTML = Number(unsafeString);

如果資料是來自外部的金額,這比直接塞 HTML 字串更安全,因為瀏覽器會把非數字的字串轉為 NaN,而不是執行腳本。


用結構化資料讓 AI 能讀懂你

TrueLink 不只是防 XSS,更是讓資料能被 AI 引用。我們在內容產線上,用結構化資料(schema.org)標註作者、發布時間、文章類型,這不僅是為了 SEO,更是為了讓 AI 引擎能讀懂你。

比如我們會標註:

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "innerHTML 是不可信的:插值前一律 escape 或 Number() 強制轉型防 XSS",
  "author": {
    "@type": "Person",
    "name": "林士華",
    "sameAs": ["https://truelink-group.com/"]
  },
  "datePublished": "2024-04-05"
}

這樣做不僅讓內容可驗證(E-E-A-T),也讓 AI 引擎能從機器可讀的結構中理解你。這才是「讓 ChatGPT 引用你的品牌」的本質。


實務上常見的 XSS 風險場景

風險場景風險來源建議做法
使用者評論使用者輸入用 DOM API 建文字節點
API 回傳 HTML 字串第三方 API拒絕直接 innerHTML 塞入,改用 DOM API 或 escape
數字欄位(如金額、數量)使用者輸入或 API用 Number() 做強制轉型
動態渲染問答內容使用者輸入或 API用 FAQPage schema 標註,並搭配 escape

發布前的十二道閘:內容是否可驗證

TrueLink 的內容產線有十二道審核閘,其中一個就是「是否機器可讀」。如果你的內容被機器當作 HTML 而非資料處理,它就不會被 ChatGPT 引用。

你可以用這幾個步驟檢查:

1. 資料來源是否可信? 來自使用者、API、第三方系統的資料,一律不信任。 2. 資料是否經過轉型或 escape? 沒有轉碼的 innerHTML 是高風險。 3. 內容是否可被 AI 引擎切片引用? 用 SVG、表格文字、schema.org 標記,而非擴散圖。 4. 是否有結構化資料支援? 沒有 schema.org 標註的內容,機器看不懂。


真正的問題不是 XSS,而是你的內容不被信任

XSS 是技術風險,但背後的本質是「你的內容不夠可信」。TrueLink 的核心價值是「AI 信任時代的數位信任基礎建設」,我們做的不是 SEO 工具,而是讓你的內容能被機器認出、引用、信任。

如果你的內容塞滿 innerHTML,它可能在人類眼裡看起來沒問題,但對 AI 引擎來說,那只是亂碼。而對客戶來說,這代表你們的品牌不會被機器認出。