場景1:用戶輸入表單(如評論框)
錯誤做法:直接渲染未過濾的用戶輸入
// WXML
<view>{{ userInput }}</view>// JS(用戶輸入了惡意內容)
Page({data: { userInput: '<script>alert("XSS攻擊")</script>' }
})
結果:小程序會自動轉義,頁面顯示為文本:<script>alert("XSS攻擊")</script>
,不會執行腳本。
防護原理:數據綁定 ({{ }}
) 默認轉義 HTML 特殊字符(如 <
→ <
)。
場景2:動態渲染富文本(如用戶發布的文章)
錯誤做法:直接用 rich-text
渲染未過濾的內容
// WXML
<rich-text nodes="{{ userContent }}"></rich-text>// JS(用戶輸入了危險內容)
Page({data: {userContent: '<img src="x" onerror="alert(1)">' // 包含惡意 onerror 事件}
})
結果:小程序 rich-text
組件會自動過濾 onerror
屬性,最終渲染成 <img src="x">
。
防護原理:rich-text
組件內置黑名單,過濾 script
、iframe
和危險屬性(如 onclick
、onerror
)。
場景3:使用 web-view
嵌入外部網頁
錯誤做法:加載不可信的第三方網頁
<web-view src="{{ externalUrl }}"></web-view>// JS(外部鏈接被篡改)
Page({data: {externalUrl: 'https://惡意網站.com?attack=...'}
})
風險:外部網頁可能含有 XSS 攻擊代碼,通過 web-view
傳播。
正確做法:
- 限制
web-view
只能加載白名單域名(在小程序后臺配置)。 - 對
src
參數做合法性校驗:
// 校驗 URL 是否合法
if (!isValidUrl(externalUrl)) {externalUrl = ''; // 拒絕加載
}
場景4:調用接口獲取數據
錯誤做法:信任后端返回的未過濾數據
// 假設后端返回數據:{ content: '<script>惡意代碼</script>' }
wx.request({url: 'api/getData',success: (res) => {this.setData({ content: res.data.content }); // 直接渲染}
})
結果:雖然小程序默認轉義,但如果數據用于不安全場景(如 web-view
),仍可能引發風險。
正確做法:
- 后端返回前對數據做 XSS 過濾。
- 前端對關鍵內容二次過濾:
// 使用微信提供的安全過濾函數(示例)
const safeContent = filterXSS(res.data.content);
this.setData({ content: safeContent });
場景5:用戶昵稱/簡介中的特殊字符
錯誤做法:允許用戶輸入任意字符
// 用戶輸入昵稱:<img src=x onerror=alert(1)>
Page({data: { nickname: userInput }
})
結果:渲染到頁面時會被轉義,但若其他地方誤用(如轉發到其他系統),可能引發風險。
正確做法:
- 輸入時過濾危險字符:
// 前端過濾
const cleanNickname = nickname.replace(/[<>"'&]/g, '');// 或調用微信內容安全接口(推薦)
wx.msgSecCheck({content: nickname,success: () => { /* 內容安全 */ },fail: () => { /* 攔截危險內容 */ }
})
總結:XSS 防護口訣
- 數據綁定用雙花:
{{ }}
默認轉義,別用innerHTML
。 - 富文本要過濾:
rich-text
或安全解析庫。 - 用戶輸入必校驗:前后端雙重過濾。
- 動態代碼要禁用:別用
eval
、new Function
。 - 外部內容嚴管控:
web-view
域名白名單。
附:XSS 測試工具
- 安全測試輸入:嘗試輸入以下內容,檢查是否被轉義:
<img src=x onerror=alert(1)> <!-- 測試HTML屬性 --> <script>alert(1)</script> <!-- 測試腳本標簽 --> javascript:alert(1) <!-- 測試URL協議 -->