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

时间:2014-03-28 20:57:21

标签: javascript asp.net-mvc security razor

我使用Newtonsoft库将C#对象转换为JSON。 Newtonsoft.Json.JsonConvert.SerializeObject使用<script type="text/javascript"> var jsModel = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)) </script> 是安全的,还是需要额外的编码?如果需要额外的编码,你有什么建议?

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

{{1}}

5 个答案:

答案 0 :(得分:12)

您至少需要执行'&lt;'的额外编码字符为'\ u003C'和'&gt;'字符为'\ u003E'。最后我检查了JSON.NET没有在字符串文字中编码这些字符。

我可能会对此感到害怕,但我这样做的方法是在页面上渲染一个虚拟元素:

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

然后,在Javascript中,从 the-div 元素和JSON.parse中提取 data-json 属性值。这样做的好处是您不必担心哪些字符需要特殊编码。 SerializeObject方法保证 JSON blob格式正确,@运算符保证保留剩余的任何非HTML安全字符在放入HTML属性之前,JSON转换中的转换被正确转义(只要属性值被双引号括起来,如上所述)。所以是的,它有点丑陋,但它可以完全关闭整个漏洞类型。

答案 1 :(得分:5)

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

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

我使用了以下非常简单的测试。如果我只是在问题中使用@Html.Raw,那么&#34; Bad&#34;警报出现。以这种方式包装,我有有效的JavaScript,并且不会出现警报。

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

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

答案 2 :(得分:3)

我制作了这个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));
    }
}

用法:

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

答案 3 :(得分:0)

我认为这不一定不安全,但这取决于数据。如果您的数据已经过清理,如果它来自外部源,它总是应该进行清理,那么您可能没问题。事实上它进入一个javascript对象并且没有呈现为HTML会让事情变得模糊不清,但它仍然会降低你对输出数据的信任程度。

答案 4 :(得分:0)

考虑根据TorbjörnHansson的黄金回答删除一行或两行代码:

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
}

这里有一些关于如何使用它的例子(以及何时不使用它):

<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 *@

最后但并非最不重要:

<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>

希望这有帮助。