2016-09-21 41 views
0

我在json中安全編碼類似html的文本時遇到了困難。文本應寫入<textarea>,由ajax傳輸到服務器(.net45 mvc)並以json字符串形式存儲在數據庫中。遍歷和HtmlEncode Json.net中的字符串C#

當傳輸到服務器時,我得到着名的「有潛在危險的Request.Form值被檢測到」500服務器錯誤。爲了避免這個消息,我使用傳遞的模型上的[AllowHtml]屬性。通過這樣做,我打開了XSS漏洞,如果有人粘貼{ "key1": "<script>alert(\"danger!\")</script>" }。因此,我想用類似

tableData.Json = AntiXssEncoder.HtmlEncode(json, true); 

問題是我不能做到這一點就滿JSON字符串,因爲它會呈現類似

{&#13;&#10;&quot;key1&quot: ...} 

這當然不是我想要的。它應該更像

{ "key1": "&lt;script&gt;alert(&quot;danger!&quot;)&lt;/script&gt;" } 

有了這個結果,用戶可以寫任何他們想要的代碼,但我能避免它被呈現爲HTML,只是顯示爲普通的文本。有誰知道如何遍歷JSON與C#(Newtonsoft Json.NET),使字符串可以編碼AntiXssEncoder.HtmlEncode(... , ....);?或者我在這裏錯了嗎?

編輯:

  1. 的數據是不均勻的,所以解串行成均勻的對象是不是一個選項。
  2. 數據可能會向公衆開放,因此存儲編碼的數據會減輕我的靈魂。
+0

看看[在反序列化過程中有選擇地轉義字符串中的HTML](http://stackoverflow.com/q/32562381/10263);這聽起來和你的情況很相似。您可能可以根據需要調整解決方案。 –

回答

0

如果你已經有了數據作爲JSON字符串,你可以使用JsonConvert.DeserializeObject()分析它與類似Json.NET合適的對象(或其他任何東西,居然有相當多的選項可供選擇)。一旦它是普通的對象,你可以通過它們並應用你想要的任何編碼,然後再將它們序列化成一個JSON字符串。你也可以看看this question及其答案。

您可能採取的另一種方法是直到實際插入頁面DOM中的東西。您可以將未編碼的數據存儲在數據庫中,甚至可以將其作爲JSON數據發送給客戶端,而無需HTML編碼(當然,它需要爲JSON編碼,但是任何序列化器都會這樣做)。您需要小心,不要直接將它以這種方式生成到頁面源代碼中,但只要它是帶有text/json內容類型的AJAX響應,就沒有問題。然後在客戶端上,當你決定將它插入到實際的textarea中時,你需要確保將它插入爲文本,而不是html。從技術上講,這可能意味着使用jQuery的.text()而不是.html(),或者你的模板引擎或客戶端的數據綁定解決方案的相關方法(text:,而不是在淘汰賽html:#:代替#=中說劍道UI等)

優勢這是後一種方法,在發送數據時,服務器(類似於API)不需要知道或關心客戶端將在何處或如何使用數據,這只是數據。客戶端可能需要不同的HTML或Javascript上下文的編碼,服務器不一定選擇正確的。

如果你知道只是需要這些數據的地方,你當然可以採取第一個(你原來的)方法,在服務器上進行編碼,這是同樣好的(有些人可能會認爲,場景)。

與回答這個問題的問題是,詳細計數很多。從理論上講,你有很多方法可以做到這一點,但有時候一個好的解決方案與一個單一角色中的脆弱解決方案有所不同。

+0

謝謝你的回答。 數據不統一,可能向公衆開放。因此,反序列化會導致動態對象,這會導致一組新的問題。由於其他人可能會使用這些數據,我發現最好立即將它們存儲爲「安全」數據。 我目前正在嘗試一種非遞歸方法來遍歷JSON字符串作爲JToken對象,並對所有值進行編碼,我發現類型等於JTokenType.String。 – pekaaw

0

所以這是我去的解決方案。我在ViewModel中添加了[AllowHtml]屬性,以便我可以從textarea發送原始html(通過ajax)。 使用此屬性,我避免了MVC提供的System.Web.HttpRequestValidationException以防範XSS危險。 後來我通過解析它作爲一個JToken遍歷JSON字符串和編碼字符串:

public class JsonUtils 
{ 
    public static string HtmlEncodeJTokenStrings(string jsonString) 
    { 
     var reconstruct = JToken.Parse(jsonString); 
     var stack = new Stack<JToken>(); 
     stack.Push(reconstruct); 

     while (stack.Count > 0) 
     { 
      var item = stack.Pop(); 
      if (item.Type == JTokenType.String) 
      { 
       var valueItem = item as JValue; 
       if(valueItem == null) 
        continue; 

       var value = valueItem.Value<string>(); 
       valueItem.Value = AntiXssEncoder.HtmlEncode(value, true); 
      } 

      foreach (var child in item.Children()) 
      { 
       stack.Push(child); 
      } 
     } 
     return reconstruct.ToString(); 
    } 
} 

生成的JSON字符串將仍然是有效的,我把它保存在數據庫中。現在,在View中打印時,我可以直接從JS中的json使用字符串。 當再次打開另一個<textarea>進行編輯時,我必須解碼html實體。爲此,我從string.js「偷」了一些js-code(decodeHtmlEntities);當然要添加許可證和信用票據。

希望這可以幫助任何人。