淺談 ASP.NET WebForm / MVC 內嵌 HTML 時的 XSS 防護
2 |
開發公佈欄、商品介紹之類的網站內容管理應用,最常見的設計是提供使用者 HTML 編輯介面(使用 WYSIWYG 編輯器或直接修改 HTML)修改內容存入資料庫,顯示時再將該段 HTML 內嵌成為網頁的一部分 (例如:在 ASP.NET MVC 使用 @Html.Raw(htmlContent)、在 ASP.NET WebForm <%= htmlContent %> / spanElement.InnerHTML = htmlContent ...),最大好處是內容編輯者可善用 HTML 語法實現各式排版效果。但 HTML 除了元素、文字內容、樣式,也能包含 JavaScript 程式,開放使用者自由輸入 HTML 內容,即有被注入惡意程式的風險。這種利用資料輸入管道注入 JavaScript 程式碼在瀏覽器執行的攻擊手法,即所謂 XSS,Cross-Site Scripting 跨網站指令碼。
一般要在網頁插入後端動態決定的純文字,只需轉為 HtmlEncode 編碼基本上就安全了,當 < 變成 <,便不會被解讀成 HTML 標籤,即便夾帶 JavaScript 也不會被執行。但內容管理應用中,動態產生的內容本質是 HTML,需直接融入網頁 HTML 的一部分才有效果,不可能套用 HtmlEncode,便得靠其他方式避開 XSS 風險。以下是一些思考方向:
- 較知名的 HTML 文件編輯器,像是 Telerik RadEditor、TinyMCE、CKEditor 設計時都會考量 XSS 議題,也會有基本防護,但過往不乏被找到漏洞的案例,故全靠它把關不夠安全。
- 如果開放編輯 HTML 的初衷只是要支援標題、標號、底線、粗體、斜體、圖檔、超連結等基本功能,那麼改用 Markdown 格式是不錯的選擇,支援基本排版樣式但無法塞入 JavaScript。不過,Markdown 普及度不如 HTML,使用者接受度可能會是問題。
- 在伺服器端過濾 HTML 內容,以白名單方式保留指定標籤及 Attribute,杜絕 JavaScript 混入(術語叫 Sanitization 消毒)。但別以為寫段程式把 <script> 換掉就沒事了,XSS 攻擊有很多變形手法,交給專業程式庫處理才是王道。
要在 .NET 過濾 XSS 惡意程式碼,NuGet 有兩套較常用的程式庫:
- ASP.NET Team 開發的 AntiXSS 620 萬次下載
微軟官方出品是最大賣點,且不依賴第三方程式庫,.NET 2.0/3.5/4.0+ 都能用,但它已偏老舊,2014 年之後就未再更新,原專案網站 wpl.codeplex.com 也已消失,僅餘
網友自己維護的非官方移植。
註:「內建 AntiXSS 程式庫」曾是 ASP.NET 4.5 標榜特色之一,但事實上 System.Web.Security.AntiXss 命名空間主要以 HtmlEncode、HtmlAttributeEncode、UrlEncode 為主,不包含 HTML 消毒淨化。 - HtmlSanitizer 995 萬次下載
處於活躍狀態的開源專案,需依賴 AngleSharp 及 AngleSharp.Css 運作,要包含哪些 HTML 標籤、Attribute、CSS 屬性可以輕鬆自訂,可避免規則過嚴誤刪必要的屬性,較符合實務需求,小缺點是它限定 .NET 4.6/.NET Standard 2.0 以上 .NET 程式才能用,不適用古蹟網站。
最後用案例展示一下 HTML 消毒功能,假設我們在 ASP.NET Index.cshtml 以 @Html.Raw() 插入一段資料庫讀取內容:
@using AntiXssTest.Models
@{
Layout = null;
var htmlFromDb = @"
<a href='javascript:alert(1)'>Click</a><br />
<img src='3' onerror=this.remove();alert(1)>
";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<fieldset>
<legend>Dangerous</legend>
<div>
@Html.Raw(htmlFromDb)
</div>
</fieldset>
<fieldset>
<legend>AntiXss</legend>
<div>
@Html.AntiXssRaw(htmlFromDb)
</div>
</fieldset>
</body>
</html>
在 htmlFromDb 中我故意在 a href、img onerror 混入 JavaScript alert(1) 等經典 XSS 攻擊,第一段用單純 Html.Raw(),第二段是加入 AntiXss 或 HtmlSanitizer 消毒功能的擴充方法 Html.AntiXssRaw()。執行結果如下,Html.Raw() 輸出結果被植入可執行的 JavaScript;而 Html.AntiXssRaw() 結果中的 JavaScript 及 onerror 被徹底清除:
Html.AntiXssRaw 擴充方法寫法如下:(Html.AntiXssRaw() 使用 Microsoft AntiXss,另外一併展示用 Ganss.XSS.HtmlSanitizer 實作的 Html.SanitizedRaw())
using Microsoft.Security.Application;
using System.Web.Mvc;
namespace AntiXssTest.Models
{
public static class HtmlHelperExtensions
{
public static MvcHtmlString AntiXssRaw(this HtmlHelper html, string rawHtml) =>
MvcHtmlString.Create(Sanitizer.GetSafeHtmlFragment(rawHtml));
static Ganss.XSS.HtmlSanitizer sanitizer = new Ganss.XSS.HtmlSanitizer();
public static MvcHtmlString SanitizedRaw(this HtmlHelper html, string rawHtml) =>
MvcHtmlString.Create(sanitizer.Sanitize(rawHtml));
}
}
以上是 ASP.NET 內嵌 HTML 時避免 XSS 攻擊的簡單介紹,設計相關應用時不要遺漏以保安康。
Tips of how to prevent XSS when embedding HTML in ASP.NET.
Comments
# by Justin
大大您好,最近使用Qualys WAS tool 去掃描asp.net project vulnerability,出現Path-Based Cross-Site Scripting (XSS)漏洞,儘管已經使用HttpUtility.HtmlEncode/Decode所有input/output,也validateRequest=true,卻還是持續報出Path-Based Cross-Site Scripting (XSS),報告有問題的路徑都是在.aspx後面插入?"><qss>=,嘗試輸入同樣的url都已經會有validateRequest exception 或是我過濾raise exception,但還是持續報出漏洞。不知道大大是否也有遇過相同的問題?
# by Jeffrey
to Justin, 我沒用過 Qualys WAS,看起來它會試著連上你的網站偵測是否有漏洞,不確定它評判標準為何,但遇到可疑存取回傳錯誤應不算漏洞吧。 另一個我想到的可能是它的偵測對象跟你測試的網站不同,故反應不同,我的話會試著從 IIS Log 驗證網站是否真的對 Qualys WAS 回應 HTTP 500,不然就得詢問 Qualys 技術支援人員了。