Day 16,我讓 Chrome 對 3 個本機表單各送出一次固定探針。三組 POST 都確實帶著 q 到達伺服器,但只有未編碼反射頁在 HTML 回應中建立 <img>,並同時留下 alert(1) 與相符的 DOM 旗標;跳脫頁只顯示探針文字,未反射頁則只顯示固定內容。
今天我不重新送出表單,而是讀取已保存的表單、請求本文、原始回應與瀏覽器結果,請 AI 填寫九欄風險報告。我想看的是它能否保留每一層證據,而不是看到 POST 本文有探針,就把三頁都稱為 XSS。
我先核對 Day 16 的 3 組來源:表單原始 HTML 要對上 action、method 和 q 欄位;POST 本文要能解析出當次完整探針;原始回應位元組要符合 SHA-256;瀏覽器 DOM、alert(1)、DOM 旗標和執行摘要也要互相一致。若任一來源被改寫,流程就拒絕產生報告。
核對後,我把每組資料整理成 5 份具名證據:
| 來源 | 報告要理解的事實 |
|---|---|
| E1 | 表單 action、POST 方法、欄位與底層工具分類 |
| E2 | 伺服器收到的 POST 請求與 q 值 |
| E3 | 原始 HTML 回應是否含 token,以及瀏覽器文字與圖片元素 |
| E4 | 對話框、DOM 旗標和雙重執行判斷 |
| E5 | 本機實驗限制 |
三組 E2 都有 request_value_matches_payload: true。真正拉開差異的是 E3 與 E4:弱點頁建立圖片且雙重訊號相符;跳脫頁的回應雖含 token,DOM 只顯示文字;未反射頁的回應不含 token。這也說明「值送達」與「值被當作 HTML 執行」不能用同一個欄位代表。
我先執行來源乾跑:
.venv/bin/python day-17/run_reports.py --dry-run
三組來源都通過,紀錄中的 api_called: false 清楚表明這一步沒有模型回答。5 個離線測試也通過:修改原始表單、POST 本文、回應、執行訊號或摘要時,驗證必須拒絕來源;報告漏掉必要引用、越過本機評級限制,或讓未確認組擴寫可能影響時,也必須拒絕。
接著我使用 gemini-3.5-flash-lite 各產生一份九欄報告,保存請求設定、API 原始回答、模型文字和驗證結果。三份回答都通過 Schema、工具分類、必要逐字引用和本機評級規則。
| 頁面 | Day 16 分類 | 模型保留的分類 | 自動檢查 |
|---|---|---|---|
| 未編碼反射 | confirmed_post_reflected_xss |
confirmed_post_reflected_xss |
通過 |
| HTML 跳脫 | post_reflected_as_text |
post_reflected_as_text |
通過 |
| 未反射 | post_not_reflected |
post_not_reflected |
通過 |
三組 risk_rating 都是 needs_context。跳脫組與未反射組的 possible_impacts 和 remediation 都是空陣列,避免把「本次探針沒有執行」擴寫成未測試的影響或修補要求。
弱點組的摘要描述與實測相符:POST 送達、回應含探針、DOM 建立圖片,且兩項執行訊號相符。但它在「可能影響」寫成「任意 JavaScript」與「受害者的瀏覽器」,又列出使用者手動點擊或受誘導的條件。Day 16 只在本機 Chrome 驗證固定 img onerror 探針,沒有測任意程式碼、真實使用者或誘導情境。我保留模型原文,另在作者語意核對紀錄標明這些超出證據的說法。
跳脫組保留了「反射為文字」的分類,沒有建立圖片;不過摘要的「確認未執行」仍須讀成「未觀察到本次固定探針的雙重執行訊號」。未反射組也正確區分了 POST 值已送達、結果頁卻沒有本次探針。兩組都不能因此被推論為整頁或整站安全。
三份 AI 報告都保住了 Day 16 的技術分類和必要引用,沒有把 POST 送達、HTML 反射、DOM 建立元素與探針執行混成一件事。但弱點組的可能影響仍超出固定本機實測,說明格式與引用驗證後還要檢查自由文字的範圍。
這次結論只適用於 Day 16 的 3 個本機 POST 表單、單一 q 欄位及固定探針;我沒有測試儲存型 XSS、其他輸入點、真實網站或正式影響。
那就…
明天見!