Json.net中的遍历和HtmlEncode字符串C#

时间:2016-09-21 12:42:43

标签: c# asp.net-mvc json.net xss html-encode

我在json中安全地编码类似html的文本。该文本应写入<textarea>,由ajax传输到服务器(.net45 mvc)并存储在json-string的数据库中。

当转移到服务器时,我得到了着名的&#34;一个潜在危险的Request.Form值被检测到&#34; 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,并将其显示为普通文本。有谁知道如何使用C#(Newtonsoft Json.NET)遍历json,以便字符串可以用AntiXssEncoder.HtmlEncode(... , ....);编码?或者我在这里走错了路?

修改:

  1. 数据不均匀,因此无法将反序列化为统一对象。
  2. 数据可能会向公众开放,因此存储编码的数据会让我的灵魂变得轻松。

2 个答案:

答案 0 :(得分:0)

如果你已经将数据作为JSON字符串,你可以使用JsonConvert.DeserializeObject()(或其他任何东西,实际上有很多选项可供选择)使用类似Json.NET的东西将其解析为适当的对象。一旦它成为普通对象,您就可以浏览它们并应用您想要的任何编码,然后再将它们序列化为JSON字符串。您还可以查看this question及其答案。

您可以采取的另一种方法是在实际将内容插入页面DOM之前不要管它。您可以将未编码的数据存储在数据库中,甚至可以将其发送到客户端,而无需将HTML编码作为JSON数据(当然,它需要针对JSON进行编码,但任何序列化程序都会这样做)。你需要注意不要以这种方式直接生成它直接进入页面源,但只要它是带有text / json内容类型的AJAX响应,它就没问题了。然后在客户端上,当您决定将其插入到实际的textarea中时,您需要确保将其作为文本插入,而不是html。从技术上讲,这可能意味着使用jQuery的.text()而不是.html(),或者您的模板引擎或客户端数据绑定解决方案的相关方法(text:在Knockout中代替html:,而在Kendo UI等中使用#:代替#=

这样做的好处是后一种方法是,在发送数据时,服务器(类似API)不需要知道或关心客户端在何处或如何使用数据,它只是数据。客户端可能需要针对HTML或Javascript上下文的不同编码,服务器不一定能选择正确的编码。

如果你知道它只是那个需要这些数据的文本区域,你当然可以采用第一种(你原来的)方法,在服务器上编码,同样好(一些)可能会说在那种情况下会更好。)

回答这个问题的问题是细节计算很多。从理论上讲,有很多方法可以做到正确,但有时一个好的解决方案不同于一个单一角色中的弱者。

答案 1 :(得分:0)

所以这就是我的解决方案。我在ViewModel中添加了[AllowHtml]属性,以便我可以从textarea(通过ajax)发送原始html。 使用此属性,我避免了MVC为防止XSS危险而提供的System.Web.HttpRequestValidationException。 然后我通过将其解析为JToken来遍历json-string并对字符串进行编码:

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-string仍然有效,我将其存储在DB中。现在,当在View中打印它时,我可以直接从JS中的json使用字符串。 当在另一个<textarea>中再次打开它进行编辑时,我必须解码html实体。为此,我从string.js“偷”了一些js-code(decodeHtmlEntities);当然要添加许可证和信用票据。

希望这有助于任何人。