iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Build on Google AI

30 天打造 AI Web Security Agent:從 Google AI Studio 到 Gemini Agent系列 第 11 篇

Day 11|把 HTML 屬性的三種結果交給 AI,報告能保留差異嗎?

  • 分享至 

  • xImage
  •  

前言

Day 10,我把查詢參數 q 放進雙引號包住的 <img src="...">,在本機得到三種不同結果:弱點頁形成獨立的 onerror 並執行探針;編碼頁雖然反射了探針文字,卻沒有新增事件屬性;不反射頁則沒有出現本次探針識別碼。

這三種狀態不能只用「回應中有沒有 onerror」來區分。編碼頁的 src 值裡仍有這段文字,但瀏覽器沒有把它當成屬性。Day 10 的實際 DOM 觀察也符合這個差別。

今天,我把這三組已保存的結果接回 Day 7 的 AI 風險報告流程。我會檢查模型是否保留工具分類,並說清楚「反射」「新增屬性」與「實際執行」各自代表什麼。

原有的證據轉換漏了什麼?

Day 7 的 build_evidence() 原本服務於 Day 6 的 HTML 文字情境。它會提供工具摘要、瀏覽器執行狀態、回應節錄與限制。我直接把三組 Day 10 JSON 交給舊函式檢查,每組仍只得到 4 份來源,但都沒有輸出位置 double_quoted_html_attribute,也沒有原始回應與 DOM 的 onerror 獨立屬性真值。

這項資訊必須由證據轉換提供。編碼頁的回應節錄包含 &quot; onerror=&quot;,如果只看字串,容易把屬性值裡的文字誤當成新的事件屬性。於是我在 Day 11 建立專用證據轉換:

來源 提供給模型的本次證據
E1 工具分類、q 參數、雙引號 img#result-image 的 src 輸出位置
E2 原始 HTML 回應、探針是否反射、解析後的 src 與獨立 onerror
E3 Chrome DOM 中的 src、onerror,以及文字是否留在 src 值內
E4 alert、對話框訊息、DOM 識別碼與執行真值
E5 本機探針的適用範圍與未測項目

程式先讀取 Day 10 的三組 JSON、原始回應文字檔及執行摘要,核對 SHA-256、HTML 解析結果、DOM 屬性與分類是否一致,再產生模型輸入。這一步只使用保存的資料,沒有重新啟動測試網站,也沒有再次送出 XSS 探針。

把最容易誤判的編碼頁拆開

編碼頁的幾個關鍵欄位分別來自原始回應、DOM 與瀏覽器執行紀錄:

E2  probe_token_reflected: true
E2  quote_entity_before_onerror: true
E2  response_handler_attribute_observed: false
E3  onerror_text_inside_src: true
E3  dom_onerror: null
E4  execution_observed: false

quote_entity_before_onerror: true 是從原始 HTML 的 &quot; onerror=&quot; 得到的可核對事實。DOM 裡的 src 含有字面上的 " onerror=",但獨立的 onerror 屬性是 null。因此,我要求報告同時引用「有反射」「沒有新增屬性」與「沒有觀察到本次探針執行」,不能只引用其中一項。

不反射頁也有自己的直接證據:probe_token_reflected: false,而原始圖片的 src 是 /fixed.png。本次識別碼沒有進入回應,分類應為 not_reflected。

第一次模型回答:分類正確,語意仍有缺口

我先用已保存的 Day 10 結果乾跑,再用 gemini-3.5-flash-lite 對三組各送出一次請求。三份回答都符合 Day 7 的九欄 JSON Schema,也保留了 confirmed_xss、reflected_but_not_executed 與 not_reflected。當時的逐字引用規則也全部通過。

不過,我逐項對照原始證據時發現:

  • 弱點組把固定 alert(1) 探針的執行寫成「執行任意 JavaScript 程式碼」,摘要又少引了部分元素與 DOM 識別碼資訊。
  • 編碼組判對了未執行,也引用了獨立屬性判斷真值;但它沒有引用 &quot; 的編碼證據或 dom_onerror: null,並把假設未來編碼失效的情境列為可能影響。
  • 不反射組寫了「無實際影響」,還建議「維持現有的輸入處理方式」。單次探針沒有反射,無法證明其他輸入的處理方式或整體影響。

根據Google 的說明,JSON 結構正確仍不保證欄位值在語意上正確,應用程式仍須驗證。這次的第一次回答正好提供了具體例子:validation.passed: true 只代表當時寫下的格式與引用規則通過,不能取代作者核對。

收緊規則,再送出一次

我保留第一次的模型原文與驗證檔,另做第二次實驗。新規則要求引用輸出元素、src、q、反射狀態、獨立屬性、alert、DOM 識別碼及限制;編碼組還須引用引號編碼與 src 值內文字,不反射組須引用固定的 /fixed.png。

在本機實驗缺少真實業務情境時,三組的 risk_rating 都必須是 needs_context。編碼與不反射組沒有確認 XSS,本次報告的 possible_impacts 和 remediation 必須為空陣列,避免把未發生的注入或猜測的伺服器處理寫成目前發現。這是報告欄位的範圍限制,並不表示這兩頁或整站已證明安全。

第二次執行的結果如下:

組別 Day 10 工具分類 模型保留的分類 必備引用 自動檢查
弱點頁 confirmed_xss confirmed_xss 13 項 通過
編碼頁 reflected_but_not_executed reflected_but_not_executed 14 項 通過
不反射頁 not_reflected not_reflected 12 項 通過

三份回答都使用 needs_context。編碼與不反射組的可能影響、修正建議皆為空。Day 11 的 20 個離線測試與 Day 7 的 9 個回歸測試也都通過;反向測試包含竄改原始回應雜湊、讓請求與回應不一致、偽造 DOM 屬性、改動工具分類及漏掉必備引用。

作者核對後,還能說到哪裡?

第二次的弱點組已把 parameter: q、attribute: src、dialog_message: 1 和 dom_marker_matches_token: true 引回來源;編碼組也引用了引號編碼、src 內的文字與 dom_onerror: null;不反射組引用了未反射及 /fixed.png。這些都比第一次完整。

但弱點組的「可能影響」仍用了「任意指令碼邏輯」一詞,條件裡的「事件處理常數」也不夠清楚。本次直接證明的是固定探針在這個本機頁面上執行,沒有逐一測試其他程式碼。即使它被放在 possible_impacts,我也不把這句當作已驗證的實測結果。編碼組摘要還直接顯示 quote_entity_before_onerror 等內部欄位名稱,公開前需要改成自然語句。

我把這些判讀另存為作者核對紀錄,模型的 report.json 與 response.txt 保持原樣。文章中的結論採用較窄的說法:弱點頁執行了本次固定探針;編碼頁的探針反射到 src 值,但沒有成為獨立事件屬性或執行;不反射頁的本次識別碼沒有出現在回應。

Day 11:讓報告知道證據在哪一層

這次,我把底層工具已觀察到的結構差異完整交給模型,並要求關鍵結論能回到指定來源。HTML 屬性值是否跳脫、是否新增事件屬性,以及瀏覽器是否執行,是三層不同的證據。

第二次模型回答保住了三組技術分類,也通過更完整的引用檢查;作者核對仍指出一處措辭超過固定探針的範圍。這份本機報告不能延伸成其他屬性、JavaScript 字串、DOM 型或儲存型 XSS 的結論,更不能替真實業務系統決定風險等級。

那就…
明天見!


上一篇
Day 10|同樣的輸入放進 HTML 屬性,瀏覽器會怎麼處理?
下一篇
Day 12|輸入進了 JavaScript 字串,反射與執行還是一回事嗎?
系列文
30 天打造 AI Web Security Agent:從 Google AI Studio 到 Gemini Agent 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言