简体   繁体   English

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

[英]Traverse and HtmlEncode strings in Json.net C#

I struggle with safely encoding html-like text in json. 我很难在json中安全地编码类似html的文本。 The text should be written into a <textarea> , transferred by ajax to the server (.net45 mvc) and stored in a database in a json-string. 文本应写入<textarea> ,由ajax传输到服务器(.net45 mvc),并以json字符串形式存储在数据库中。

When transferring to server, I get the famous "A potentially dangerous Request.Form value was detected" 500 server error. 转移到服务器时,出现著名的“检测到一个潜在危险的Request.Form值” 500服务器错误。 To avoid this message, I use the [AllowHtml] attribute on the model that are transferred. 为避免此消息,我在传输的模型上使用[AllowHtml]属性。 By doing so I open up for XSS-vulnerability, in case anyone paste in { "key1": "<script>alert(\\"danger!\\")</script>" } . 这样,我开放了XSS漏洞,以防万一有人粘贴{ "key1": "<script>alert(\\"danger!\\")</script>" } As such, I would like to use something like 因此,我想使用类似

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

Problem is I cannot do this on the full json string, as it will render something like 问题是我无法在完整的json字符串上执行此操作,因为它将呈现类似

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

which of course is not what I want. 当然不是我想要的。 It should be more like 应该更像

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

With this result the user can write whatever code they want, but I can avoid it to be rendered as html, and just display it as ordinary text. 使用此结果,用户可以编写所需的任何代码,但是我可以避免将其呈现为html,而仅将其显示为普通文本。 Does anyone know how to traverse json with C# (Newtonsoft Json.NET) such that strings can be encoded with AntiXssEncoder.HtmlEncode(... , ....); 有谁知道如何使用C#(Newtonsoft Json.NET)遍历json,以便可以使用AntiXssEncoder.HtmlEncode(... , ....);编码字符串AntiXssEncoder.HtmlEncode(... , ....); ? Or am I on a wrong track here? 还是我在这里走错了路?

Edit: 编辑:

  1. The data is non-uniform, so deserialization into uniform objects is not an option. 数据是非统一的,因此将序列化为统一对象是不可行的。
  2. The data will probably be opened to the public, so storing the data encoded would ease my soul. 数据可能会向公众开放,因此存储编码后的数据将使我的心灵放松。

If you already have the data as a JSON string, you could parse it into proper objects with something like Json.NET using JsonConvert.DeserializeObject() (or anything else, there are actually quite a few options to choose from). 如果您已经拥有作为JSON字符串的数据,则可以使用JsonConvert.DeserializeObject()使用Json.NET将其解析为适当的对象(或其他任何方法,实际上有很多选择)。 Once it's plain objects, you can go through them and apply any encoding you want, then serialize them again into a JSON string. 一旦是普通对象,您就可以遍历它们并应用所需的任何编码,然后再次将它们序列化为JSON字符串。 You can also have a look at this question and its answers. 您也可以查看此问题及其答案。

Another approach that you may take is just leave it alone until actually inserting stuff into the page DOM. 您可能采取的另一种方法是,不理会它,直到将内容实际插入页面DOM中为止。 You can store unencoded data in the database, you can even send it to the client without HTML encoding as JSON data (of course it needs to be encoded for JSON, but any serializer does that). 您可以将未编码的数据存储在数据库中,甚至可以将未经编码的数据作为JSON数据发送到客户端(当然,它需要针对JSON进行编码,但是任何序列化程序都可以这样做)。 You need to be careful not to generate it this way directly into the page source though, but as long as it's an AJAX response with text/json content type, it's fine. 您需要注意不要以这种方式直接将其生成到页面源中,但是只要它是具有text / json内容类型的AJAX响应,就可以了。 Then on the client, when you decide to insert it into the actual textarea, you need to make sure you insert it as text, and not html. 然后在客户端上,当您决定将其插入到实际的textarea中时,需要确保将其作为文本而不是html插入。 Technically this could mean using jQuery's .text() instead of .html() , or your template engine's or client-side data binding solution's relevant method ( text: instead of html: in Knockout, #: instead of #= in say Kendo UI, etc.) 从技术上讲,这可能意味着使用jQuery的.text()而不是.html() ,或模板引擎或客户端数据绑定解决方案的相关方法( text:而不是html:在Knockout中, #:而不是Kendo UI中的#=等)

The advantage of this is latter approach is that when sending the data, the server (something like an API) does not need to know or care about where or how a client will use the data, it's just data. 后一种方法的优点是,在发送数据时,服务器(类似于API)不需要知道或关心客户端将在何处或如何使用数据,而仅仅是数据。 The client may need different encoding for an HTML or a Javascript context, the server cannot necessarily choose the right one. 客户端可能需要针对HTML或Javascript上下文的不同编码,服务器不一定必须选择正确的编码。

If you know it's just that text area though where this data is needed, you can of course take the first (your original) approach, encode it on the server, that's equally good (some may argue that's even better in that scenario). 如果您知道只是需要数据的那个文本区域,那么您当然可以采用第一种(您的原始方法),在服务器上对其进行编码,这同样不错(有些人可能会认为在这种情况下会更好)。

The problem with answering this question is that details count a lot . 回答这个问题的问题在于细节很重要 In theory, there are a myriad of ways you could do it right, but sometimes a good solution differs from a vulnerable one in one single character. 从理论上讲,有许多方法可以正确地完成它,但是有时候一个好的解决方案不同于一个脆弱的解决方案。

So this is the solution I went for. 这就是我追求的解决方案。 I added the [AllowHtml] attribute in the ViewModel, so that I could send raw html from the textarea (through ajax). 我在ViewModel中添加了[AllowHtml]属性,以便可以从textarea(通过ajax)发送原始html。 With this attribute I avoid the System.Web.HttpRequestValidationException that MVC gives to protect against XSS dangers. 使用此属性,我避免了MVC为防止XSS危险而提供的System.Web.HttpRequestValidationException Then I traverse the json-string by parsing it as a JToken and encode the strings: 然后,我将其解析为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();
    }
}

The resulting json-string will still be valid and I store it in DB. 生成的json-string仍然有效,我将其存储在DB中。 Now, when printing it in a View, I can use the strings directly from json in JS. 现在,在视图中打印时,我可以直接使用JS中json中的字符串。 When opening it again in another <textarea> for editing, I have to decode the html entities. 当在另一个<textarea>再次打开它进行编辑时,我必须解码html实体。 For that I "stole" some js-code (decodeHtmlEntities) from string.js ; 为此,我从string.js中 “偷走”了一些js代码(decodeHtmlEntities); of course adding the licence and credit note. 当然要添加许可证和信用证。

Hope this helps anyone. 希望这对任何人有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM