技術交流

Fortify SCA 實作:如何使用 AntiXSSEncoder 防止 XSS 漏洞

陳志忠 陳志忠

當資料從一個不可信任的來源進入 Web 應用程式中,在尚未對包含資料的動態內容進行驗證的情況下,便將它傳送給網頁的使用者,如果資料中含有惡意程式碼(通常是 JavaScript),這時就會導致瀏覽器執行惡意程式碼。在 Persistent XSS 案例中,不可信任的資料來源通常是後端資料庫或其他後端資料儲存區,而在 Reflected XSS 案例中,來源通常會是網頁要求。

本文將以 Persistent XSS 為例,說明在 ASP.NET Web 應用程式中,如何透過 AntiXSSEncoder 類別所提供的方法來防止 XSS。

如何使用 AntiXSSEncoder 類別編碼方法防止 XSS?

首先在資料庫的 USER_INFO 資料表新增二筆測試資料,其中第二筆資料故意將 USER_NAME 的值輸入一段 JavaScript,用來測試 Web 應用程式中的 XSS: Persistent 漏洞。

USER_INFO 資料表新增二筆測試資料

圖Ⅰ是一個簡單的 Web 應用程式程式碼片段(XSSSample.aspx.cs,完整的程式碼下載點),以 EMP_ID 查詢資料庫中的 USER_INFO 資料表取得使用者資料,接著將 USER_NAME 分別顯示於一個 Label 控制項(lblUserName)及一個 TextBox 控制項(tbxUserName),如圖Ⅰ的 Line 31 及 Line 32。

圖Ⅰ。範例程式碼片段

繼續將 USER_NAME 以 AntiXssEncoder.HtmlEncoder 方法編碼後,再分別顯示於另一個 Label 控制項(lblUserNameWithAntiXSS)及另一個 TextBox 控制項(tbxUserNameWithAntiXSS),如圖Ⅰ的 Line 33 及 Line 34。

接著使用 Fortify SCA 掃描這個 Visual Studio 專案,檢視掃描報告發現在 XSSSample.aspx.cs 的 Line 31 發現一個 Critical 等級的 Cross-Site Scripting: Persistent 漏洞。

掃描報告

觀察點 1:

Line 31 及 Line 32 的差別只在於 Line 31 是 Label 控制項,而 Line 32 則是 TextBox 控制項,為何只有 Line 31 被指出有 XSS: Persistent 漏洞?

部署這個 Web 應用程式到 IIS 並啟動後,首先使用瀏覽器測試網頁(XSSSample.aspx)。

使用瀏覽器測試

接著在 Emp. ID 欄位輸入 1234 後點擊 Get User Info. 按鈕,結果馬上跳出了警告視窗。

警告視窗

點擊確定後完成測試。

點擊確定

此時利用瀏覽器的檢視原始碼功能檢視測試網頁,可以看見 LblUserName 及 tbxUserName 的 HTML 程式碼片段如下:

HTML 程式碼片段

id 為 LblUserName 的元素因 innerHTML 而變成一段跳出「攻擊成功」警告視窗的 JavaScript 程式碼;而 tbxUserName 的 value 屬性值已被編碼過,不再是可執行的 JavaScript,所以並沒有造成攻擊。

此處解答了觀察點 1,也就是 TextBox 控制項預設會對屬性值進行編碼,但是 Label 控制項則不會,所以 XSSSample.aspx.cs 的 Line 31 會被 Fortify SCA 掃描出 XSS: Persistent 漏洞,而 Line 32 則不會。

接著我們再繼續檢視 lblUserNameWithAntiXSS 的 HTML 程式碼片段如下:

HTML 程式碼片段

因為在 XSSSample.aspx.cs 的 Line 33 將 USER_NAME 以 AntiXssEncoder.HtmlEncode 方法進行編碼後,才顯示於 Label 控制項(lblUserNameWithAntiXSS),因此可以看到 id 為 lblUserNameWithAntiXSS 的元素其 innerHTML 不再是可執行的 JavaScript,因此可使用 AntiXssEncoder 的編碼方法來防止 XSS: Persistent,而這個做法也適用於防止 XSS: Reflected。

現在我們再回到 Fortify SCA 掃描報告上,在 XSSSample.aspx.cs 的 Line 33 發現有一個 Medium 等級的 Cross-Site Scripting: Poor Validation 漏洞。

Fortify SCA 掃描報告

觀察點 2:

圖Ⅰ的 Line 33 使用 AntiXssEncoder.HtmlEncoder 方法對 USER_NAME 編碼,雖然已不再出現 Critical 等級的 XSS: Persistent 漏洞,但卻又跑出來一個 Medium 等級的 XSS: Poor Validation 漏洞?

節錄 Fortify SCA 對此情況的說法如下:

使用特定編碼功能可防止部分,但不是所有的 Cross-Site Scripting 攻擊。根據資料出現的內容,以 HTML 編碼的基本 <、>、& 和 “,以及以 XML 編碼的 <、>、&、“ 和 ' 以外的字元,可能會有中繼意義。依賴這類編碼功能等同於使用安全性較差的拒絕清單來防止 Cross-Site Scripting 攻擊,並且可能會允許攻擊者插入隨後會在瀏覽器中執行的惡意程式碼。因為並非隨時都可以準確識別其中靜態顯示資料的內容,所以即使套用編碼,Fortify Secure Coding Rulepack 還是會報告 Cross-Site Scripting 發現,並將其顯示為 Cross-Site Scripting: Poor Validation 問題。

對比觀察點 1,TextBox 控制項因為預設會對屬性值編碼,因此 Line 32 並不會被 Fortify SCA 掃描出漏洞,但是在看過 TextBox 控制項的原始碼之後,發現其做法與 AntiXssEncoder.HtmlEncoder 方法大致相同,所以無法理解為何 Line 33 會出現 XSS: Poor Validation,而 Line 32 卻不會。

如果對於 AntiXssEncoder 類別的各種編碼方式有著與 Fortify SCA 同樣的疑慮,建議實作一個安全字元的白名單,只允許白名單上的字元出現在 HTTP Content,並僅接受白名單中字元組成的輸入。

使用 MarkAsSafe 方法將中文字碼表加入安全字元清單

使用 AntiXssEncoder 類別的編碼方法後可能會遇到的一個問題:非英文字元的編碼。

首先在測試網頁的 Emp. ID 欄位輸入 0628 後點擊 Get User Info. 按鈕。

輸入 0628

再利用瀏覽器的檢視原始碼功能檢視測試網頁,可以看到 LblUserNameWithAntiXSS 及 tbxUserNameWithAntiXSS 的 HTML 程式碼片段如下:

程式碼片段

可看到 #LblUserNameWithAntiXSS 元素,其 innerHTML 是 &#38515;&#24535;&#24544;,並不是網頁上看到的陳志忠。這是因為在 XSSSample.aspx.cs 的 Line 33 將陳志忠編碼,瀏覽器在處理 #LblUserNameWithAntiXSS 元素的 innerHTML 時會自動處理已編碼部分,所以在前端網頁上看到的仍是陳志忠,但是在後端程式中,lblUserNameWithAntiXSS.Text 的值是 &#38515;&#24535;&#24544;。若直接取用其值,將不是眼睛看到的陳志忠,而是 &#38515;&#24535;&#24544;

AntiXssEncoder 類別預設標記為安全字元的 Unicode, 字碼表為 LowerCodeCharts.Default,共包含以下字碼表,除以下字碼表中的字元外,其他字元通通會被編碼,也就是說中文字元也會被編碼:

  • BasicLatin
  • C1ControlsAndLatin1Supplement
  • LatinExtendedA
  • LatinExtendedB
  • IpaExtensions
  • SpacingModifierLetters
  • CombiningDiacriticalMarks

加入中文字碼表

如果要將 AntiXssEncoder 類別加入中文的支援,就必須加入中文的字碼表,作法如下:

在 Global.asax.cs 的 Application_Start 方法中,使用 AntiXssEncoder.MarkAsSafe 方法將中文字碼表加入安全字元清單。如下圖所示,除了 LowerCodeCharts.Default 外,還加上了 UpperMidCodeCharts.CjkRadicalsSupplement、UpperMidCodeCharts.KangxiRadicals、UpperMidCodeCharts.Bopomofo、UpperMidCodeCharts.CjkUnifiedIdeographsExtensionA、UpperMidCodeCharts.CjkUnifiedIdeographs、UpperMidCodeCharts.LatinExtendedD 及 UpperCodeCharts.CjkCompatibilityIdeographs 等與中文相關的字碼表。

中文字碼表

【重要】:安全字元清單的改變會影響所有 HTML、XML、CSS 和 URL 編碼方法。

接著重新部署 Web 應用程式到 IIS 並重新啟動後,使用瀏覽器測試網頁(XSSSample.aspx),在 Emp. ID 欄位輸入 0628 後點擊 Get User Info. 按鈕。

瀏覽器測試呈現陳志忠

lblUserNameWithAntiXSS 及 tbxUserNameWithAntiXSS 二個控制項都顯示陳志忠,再次利用瀏覽器的檢視原始碼功能,lblUserNameWithAntiXSS 及 tbxUserNameWithAntiXSS 的 HTML 程式碼片段如下,也是呈現陳志忠,代表 AntiXssEncoder 類別在加入中文的支援後,使用 AntiXssEncoder.EncodeHtml 方法編碼時,中文字元已不再被編碼,這樣就可以正確取得 lblUserNameWithAntiXSS.Text 或 tbxUserNameWithAntiXSS.Text 使用。

編碼結果

結語

以上使用一個簡單的 Web 應用程式示範如何造成 XSS,以及使用 AntiXSSEncoder 類別的編碼方法來防止 XSS,同時也示範如何使用 AntiXSSEncoder 類別的 MarkAsSafe 方法將中文字碼表加入安全字元清單中,看完後是否對防止 XSS 漏洞有多一點瞭解呢?其實防止 XSS 弱點的方法有很多種,但所有方法的重點就是:驗證所有進入應用程式的資料(Input),以及所有離開應用程式並傳送到使用者的資料(Output)