Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2311

ASP.NET MVC整合RichText編輯器範例與注意事項

$
0
0

最近的ASP.NET MVC專案用到了RichText編輯器,允許使用者編輯包含不同字型、大小、粗細、顏色的格式化文字,其中有些需注意細節,整理筆記備忘。

網頁版RichText編譯器的選擇不少,本文以KendoEditor為例,結果則以PostBack方式回傳。即使換用其他編輯器或改以AJAX回傳,ASP.NET MVC整合重點大同小異。

範例的MVC網站共有Index及Result兩個View,Index為編輯器頁面,Result則用來顯示結果。Controller除了Index及Result兩個Action,再增加一個Sumbit Action,負責接受前端送回內容,模擬將結果寫入DB(為求簡化,以保存在記憶體替代)供Result View讀取顯示,接著導向Result View顯示編輯結果。

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace Mvc.Controllers
{
publicclass HomeController : Controller
    {
staticstring _content = string.Empty;
void SaveToDb(string content)
        {
//模擬寫入DB
            _content = content;
        }
string ReadFromDb()
        {
//模擬由DB讀取
return _content;
        }
 
 
public ActionResult Index()
        {
return View();
        }
 
        [HttpPost]
        [ValidateInput(false)]
public ActionResult Submit(string content)
        {
            SaveToDb(content);
return RedirectToAction("Result");
        }
 
public ActionResult Result()
        {
            ViewBag.Content = ReadFromDb();
return View();
        }
    }
}

Index.cshtml已盡量簡化,網頁只有一個KendoEditor及一顆送出鈕,送出前透過JavaScript取出編輯結果(HTML)存入<input type="hidden" name="content" />,傳送給Submit Action接收:

 
@{
    Layout = null;
}
 
<!DOCTYPEhtml>
 
<html>
<head>
<metaname="viewport"content="width=device-width"/>
<title>Kendo Editor Test</title>
<linkrel="stylesheet"
href="//kendo.cdn.telerik.com/2016.2.714/styles/kendo.common.min.css"/>
<linkrel="stylesheet"
href="//kendo.cdn.telerik.com/2016.2.714/styles/kendo.default.min.css"/>
<scriptsrc="//kendo.cdn.telerik.com/2016.2.714/js/jquery.min.js"></script>
<script src="//kendo.cdn.telerik.com/2016.2.714/js/kendo.all.min.js"></script>
</head>
<body>
<div>
@using (Html.BeginForm("Submit", "Home"))
        {
<textarea id="editor" style="width: 480px; height: 200px;">
黑暗執行緒
</textarea>
<input type="hidden" id="content" name="content" />
<button id="submit" type="submit">Submit</button>
        }
</div>
 
<script>
        $("#editor").kendoEditor({
            tools: [
"formatting",
"bold",
"italic",
"underline",
"strikethrough",
"foreColor",
"backColor"
            ]
        });
var editor = $("#editor").data("kendoEditor");
        $("#submit").click(function () {
            $("#content").val(editor.value());
        });
</script>
</body>
</html>

Result.cshtml也很單純,在Server端將HTML內容存入ViewBag.Content,View裡以@ViewBag.Content顯示的結果經過HtmlEncode處理(<變成&lt;)可呈現HTML原始碼,@Html.Raw(ViewBag.Content)則將HTML內容變成網頁一部分,可呈現HTML裡<h1>、<span style="color:#444">等樣式效果。注意:Html.Raw()允許使用者輸入內容成為網頁HTML語法的一部分,跟SQL Injection漏洞原理相仿,存在被注入惡意程式碼的風險,使用時需嚴加防範攻擊!這部分後面再說明。

 
@{
    Layout = null;
}
 
<!DOCTYPEhtml>
 
<html>
<head>
<metaname="viewport"content="width=device-width"/>
<title>結果顯示</title>
<style>fieldset { width: 400px; height: 120px; }</style>
</head>
<body>
<fieldset>
<legend>輸入內容</legend>
<div>@ViewBag.Content</div>
</fieldset>
<fieldset>
<legend>HTML顯示結果</legend>
<div>@Html.Raw(ViewBag.Content)</div>
</fieldset>
</body>
</html>

就這樣,一個提供使用者編輯格式化文字內容的網頁介面就完成了。

接下來,來談談幾個需要注意的地方。

第一,Submit Action宣告為[HttpPost],不允許以GET方式執行。原因:永遠不要使用GET方式接收指令進行資料更新!

第二,在ActionResult Submit(string content)上有個[ValidateInput(false)],目的在關閉Request內容檢核。基於安全考量,ASP.NET MVC預設會攔截包含XML標籤的Request內容,避免有心人士透過Action注入XSS攻擊程式。但在RichText編輯情境,content包含HTML是正常的,若不設定[ValidateInput(false)]停用檢核機制,送出資料時會出現錯誤:

具有潛在危險Request.Form的值已從用戶端(content="<h2><span style="col…")偵測到。

關閉ValidateInput代表我們預期並接受content參數包含HTML語法,但於此同時也開始要承擔「content內容可能包藏XSS攻擊」風險。等等,KendoEditor並不容許輸入<script>、<iframe>,使用者應該沒法搞怪吧?錯!只要資料來自前端由使用者提供,就處處隱藏殺機,例如以下XSS注入示範:

不需用特殊道具,瀏覽器開啟F12跑一行指令,即可篡改傳送內容加入惡意程式碼,若Result View是公眾瀏覽的頁面,就可能被當成發動攻擊的跳板。

第三點,要防止使用者輸入HTML夾帶惡意程式,最有效的方法是使用Sanitizer工具進行過濾,只保留白名單列舉的HTML標籤,排除可能夾帶惡意內容的管道。至於過濾工具,過去大家蠻常用的AntiXSS Library Sanitizer,處於3.x版不夠安全,4.x版把不該殺的也殺光光的尷尬處境(4.x版被一顆星評價洗版),已不再是好選擇。重新評估,我選擇較活躍的開源專案-HtmlSanitizer

可使用NuGet安裝:

裝妥後在Submit()加上content = new HtmlSanitizer().Sanitize(content),即可過濾content可能有害的內容,前述示範惡意插入的JavaScript會整段被移除。

[HttpPost]
[ValidateInput(false)]
public ActionResult Submit(string content)
{
    content = new HtmlSanitizer().Sanitize(content);
    SaveToDb(content);
return RedirectToAction("Result");
}

重新整理重點:

  • Razor語法插入後端內容時預設會經過HtmlEncode,基本上能有效防止XSS攻擊。但RichText在呈現時必須原始呈現,需使用@Html.Raw()嵌入頁面。使用Html.Raw()代表使用者輸入內容有可能成為網頁HTML一部分,務必從嚴檢核,防範被插入惡意程式。
  • 接收資料進行更動作業的Action宜加上[HttpPost]降低被攻擊機率。
  • 接收HTML資料的Action需加上[ValidateInput(false)],避免資料傳送被封鎖。
  • HTML內容進入系統前應使用Sanitizer濾掉可能有害部分。

Viewing all articles
Browse latest Browse all 2311

Trending Articles