在輸出網頁時內嵌 JSON 轉成 JavaScript 物件是我愛用的手法,這點之前有介紹過,例如以下範例:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script>
        var DataItem = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.DataItem));
    </script>
</head>
<body>
    <div> 
    </div>
</body>
</html>

不需額外寫程式,C# 端 DataItem 物件直接就轉成 JavaScript,簡潔方便,看起來沒什麼問題。

但以上寫法不夠嚴謹,讓我們動點手腳搞壞它:

public class HomeController : Controller
{
	public ActionResult Index()
	{
		var item = new
		{
			Index = 255,
			Name = "BOOM!!! </script>",
		};
		ViewBag.DataItem = item;
		return View();
	}
}

轟! JSON 中出現 &lgt;/script> 與上方的 &lgt;script> 配對成功,提前結束 script 區塊,JavaScript 程式不完整噴出錯誤,後方的程式碼混進 HTML 造成網頁錯亂:(提醒:若 JSON 內容由使用者輸入且未過濾 HTML 標籤,將存在被 XSS 攻擊的風險,請務必修正。另外,使用 Html.Raw() 時千萬要謹慎。)

寫過 Inline ASPX 的老人們都知道要避開這個雷(像是這篇文章出現的Response.Write("<script>parent.showMsg('" + msg + "');<" + "/script>");),在 <script> 內嵌內容時 JSON 卻容易疏忽。Json.NET 有個 Newtonsoft.Json.StringEscapeHandling.EscapeHtml 參數,在 SerializeObject() 時傳入 JsonSerializerSettings 指定 StringEscapeHandling = StringEscapeHandling.EscapeHtml 即可輕易解決。

<script>
    var DataItem = @Html.Raw(
                  Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.DataItem,
                  new Newtonsoft.Json.JsonSerializerSettings
                  {
                      StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml
                  }));
</script>

啟用 StringEscapeHandling.EscapeHtml 後,< > 將被轉碼為 \u003c、\u003e,可避免字串內含 HTML 標籤干擾網頁解析。

如果無特殊需求,建議直接修改 JsonConvert.DefaultSettings 讓 Json.NET 預設啟用 EscapeHtml,做法可在 Global.asax.cs 或 App_Start 類別方法修改預設值,如此可避免忘記調設定出錯:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings 
{ 
    StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml
};

最後,順便看一下 ASP.NET Core。ASP.NET Core 預設已改用 System.Text.Json官方文件有提到 System.Text.Json 原本就會換掉特殊字 字(甚至包含中文),這點之前我已見識過 - ASP.NET Core JSON 中文編碼問題與序列化參數設定,只要不要亂設 JsonSerializerOptions.Encoder (例如:JavaScriptEncoder.UnsafeRelaxedJsonEscaping),應不致遇到問題。

This aritle introduce the script tag escaping issue while embedding JSON in HTML, and how to avoid it.


Comments

Be the first to post a comment

Post a comment