javascript JsonConvert.SerializeObject 的输出是否需要在 Razor 视图中进行编码?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22723077/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 23:39:59  来源:igfitidea点击:

Does the output of JsonConvert.SerializeObject need to be encoded in Razor view?

javascriptasp.net-mvcsecurityrazor

提问by Jeremy Cook

I use the Newtonsoft library to convert C# objects into JSON. Is this use of Newtonsoft.Json.JsonConvert.SerializeObjectsecure, or is additional encoding necessary? If additional encoding is needed, what do you suggest?

我使用 Newtonsoft 库将 C# 对象转换为 JSON。这种使用是Newtonsoft.Json.JsonConvert.SerializeObject安全的,还是需要额外的编码?如果需要额外的编码,你有什么建议?

Here is how I use it in a Razor view:

这是我在 Razor 视图中使用它的方法:

<script type="text/javascript">
    var jsModel = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model))
</script>

采纳答案by Levi

You will at the very least need to perform additional encoding of the '<' character to '\u003C' and the '>' character to '\u003E'. Last I checked JSON.NET did not encode these characters in string literals.

您至少需要将 '<' 字符编码为 '\u003C',将 '>' 字符编码为 '\u003E'。最后我检查了 JSON.NET 没有在字符串文字中对这些字符进行编码。

I'm probably going to get flak for this, but the way I would do this is to render a dummy element onto the page:

我可能会为此受到抨击,但我这样做的方法是在页面上呈现一个虚拟元素:

<div id="the-div" data-json="@JsonConvert.SerializeObject(Model)" />

Then, in Javascript, extract the data-jsonattribute value from the the-divelement and JSON.parseit. The benefit to this is that you don't need to worry about which characters require special encoding. The SerializeObjectmethod guaranteesthat the JSON blob is well-formed, and the @operator guaranteesthat any remaining non-HTML-safe characters left over from the JSON conversion are properly escaped before being put into the HTML attribute (as long as the attribute value is surrounded by double quotes, as above). So yes, it's a little uglier, but it is effective at completely shutting down an entire class of vulnerabilities.

然后,在 Javascript 中,从the-div元素和它提取data-json属性值。这样做的好处是您无需担心哪些字符需要特殊编码。该方法保证JSON blob 格式正确,并且操作符保证在将 JSON 转换遗留下来的任何剩余的非 HTML 安全字符在放入 HTML 属性之前被正确转义(只要属性值被包围通过双引号,如上)。所以是的,它有点丑陋,但它可以有效地完全关闭一整类漏洞。JSON.parseSerializeObject@

回答by Jeremy Cook

Using @Html.Rawalone like the question does is definitely dangerous. Here is another way to safely output a model within <script></script>tags. I followed @Levi's example to depend on the browser's faculties, as well as Microsoft's security features, and came up with this:

@Html.Raw像问题一样单独使用绝对是危险的。这是在<script></script>标签内安全输出模型的另一种方法。我按照@Levi's 的例子来依赖浏览器的功能,以及微软的安全功能,并想出了这个:

var jsModel = JSON.parse("@Html.Raw(HttpUtility.JavaScriptStringEncode(
    JsonConvert.SerializeObject(Model)
))");

I used the following very simple test. If I were only using @Html.Rawlike in the question the "Bad" alert appears. Wrapped up in this way, I have valid JavaScript and the alert does not appear.

我使用了以下非常简单的测试。如果我只@Html.Raw在问题中使用like,则会出现“Bad”警报。以这种方式结束,我有有效的 JavaScript 并且没有出现警报。

var jsModel = JSON.parse("@Html.Raw(HttpUtility.JavaScriptStringEncode(
    JsonConvert.SerializeObject(new {
        Test = "</script><script>var test = alert('Bad')</script>"
    })
))");

The next step would be to wrap this up in a reusable HtmlHelper extension method.

下一步是将其封装在可重用的 HtmlHelper 扩展方法中。

回答by Torbj?rn Hansson

I made this JsonConverter that encodes all strings with Microsoft Web Protection Library-library (aka AntiXSS-library) (http://wpl.codeplex.com/):

我制作了这个 JsonConverter,它使用 Microsoft Web Protection Library-library(又名 AntiXSS-library)(http://wpl.codeplex.com/)对所有字符串进行编码:

/// <summary>
/// To be used when you're going to output the json data within a script-element on a web page.
/// </summary>
public class JsonJavaScriptEncodeConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return reader.Value;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(Microsoft.Security.Application.Encoder.JavaScriptEncode((string)value, true));
    }
}

Usage:

用法:

<script type="text/javascript">
    var jsModel = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model, new JsonJavaScriptEncodeConverter()))
</script>

回答by XDS

Thought to drop a line of code or two based on Torbj?rn Hansson's golden answer:

想过根据 Torbj 删除一两行代码吗?rn Hansson 的黄金回答:

public static class U
{
    private static readonly GeneralPurposeJsonJavaScriptEncodeConverter _generalEncoder = new GeneralPurposeJsonJavaScriptEncodeConverter();
    static public IHtmlString Js(this object obj) => new HtmlString(JsonConvert.SerializeObject(obj, _generalEncoder));

    private sealed class GeneralPurposeJsonJavaScriptEncodeConverter : JsonConverter //0
    {
        private static readonly Type TypeOfString = typeof(string);

        public override bool CanConvert(Type objectType) => objectType == TypeOfString;
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => reader.Value;
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteRawValue(Microsoft.Security.Application.Encoder.JavaScriptEncode((string) value, emitQuotes: true)); //1
    }
    //0 https://stackoverflow.com/a/28111588/863651   used when we need to burn raw json data directly inside a script element of our html like when we do when we use razor
    //1 note that the javascript encoder will leave nonenglish characters as they are and rightfully so   apparently the industry considers text in html attributes and inside
    //  html text blocks to be a battery for potential xss exploits and this is why the antixsslib applies html encoding on nonenglish characters there but not here   one
    //  could make the claim that using unicode escape sequences here for nonenglish characters could be potentionally useful if the clients receiving the server html response
    //  do not support utf8   however in our time and age clients that dont support utf8 are rarer than hens teeth so theres no point going this direction either
}

And here are some examples on how to use it (and when not to use it):

以下是有关如何使用它(以及何时不使用它)的一些示例:

<span>
            @someStringWhichMightContainQuotes @* no need to use .Js() here *@
</span>

@* no need to use .Js() here *@
<input value="@someStringWhichMightContainQuotes" />

@* no need to use .Js() here either - this will work as intended automagically *@
@* notice however that we have to wrap the string in single-quotes *@
<button   onclick="Foobar( '@("abc  \"  '  ")'  )"> Text </button>

@* The resulting markup will be:
            <button onclick="Foobar(  'abc &quot; &#39; '  )"> Text </button>
Which will work as intended *@

And last but not least:

最后但并非最不重要:

<script type="text/javascript">
    someJsController.Init({
        @* containerSelector: “#@(containerId.Js())”,  ← wrong  dont do this *@

        containerSelector: “#” + @(containerId.Js()),  @* ← correct  *@
        containerSelector2: @($"#{container2Id}".Js()),  @* ← even better do this for readability *@

        simpleString: @(Model.FilterCode.Js()), @* all these will serialize correctly *@
        someArray: @(Model.ColumnsNames.Js()), @* by simply calling the .js() method *@
        someNumeric: @(Model.SelectedId.Js()),
        complexCsharpObject: @(Model.complexCsharpObject.Js())
    });
</script>

Hope this helps.

希望这可以帮助。

回答by Brad Gardner

I don't think it's necessarily unsafe here but it depends on the data. If you data has been sanitized, which it always should if it came from an outside source, then you are probably fine. The fact that it's going into a javascript object and not rendered as HTML obscures things a bit but it still comes down to your level of trust with the data being output.

我认为这里不一定不安全,但这取决于数据。如果您的数据已经过消毒(如果它来自外部来源,则始终应该如此),那么您可能没问题。它进入一个 javascript 对象而不是呈现为 HTML 的事实使事情变得有些模糊,但它仍然归结为您对正在输出的数据的信任程度。