4

Long story short, I'm trying to get the output from JsonConvert.SerializeObject to be sanitized without having to modify the contents of the saved data.

I'm working on an app that has the following markup in the view:

                 <textarea data-bind="value: aboutMe"></textarea>

If I save the following text, I run into problems:

                 <script type="text/javascript">alert("hey")</script>

The error I get in FF:

enter image description here

The relevant part of the offending rendered text:

$(document).ready(ko.applyBindings(new MyProfileVm({"profileUsername":"admin","username":"Admin","aboutMe":"alert(\"hey\")","title":"Here's a short self-bio! :)","thumbnail":"https://i.imgur.com/H1HYxU9.jpg","locationZip":"22182","locationName":"Vienna, VA"

And finally - at the bottom of my view:

<script type="text/javascript">
    $(document).ready(ko.applyBindings(new MyProfileVm(@Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() })))));
</script>

Here, I'm passing the model that I get from the MVC controller into the js ViewModel for knockout to map into observable data. The Raw encoding seems to be the problem, but I'm not sure how to go about handling it.

To be clear, I'm getting data from the server, and outputting it to the client, which is mucking up the JSON/KO combo.

4

2 回答 2

5

The problems is that you cannot have a closing </script> tag inside a JavaScript string literal because the browser interprets it as then end of the script block. See also: Script tag in JavaScript string

There is no builtin function in Asp.Net what could handle it on the server side you before outputting your generated script you need to replace the </script> to something else:

<script type="text/javascript">
    $(document).ready(ko.applyBindings(new MyProfileVm(@Html.Raw(
        JsonConvert.SerializeObject(Model, 
            new JsonSerializerSettings() { 
                 ContractResolver = new CamelCasePropertyNamesContractResolver() 
        }).Replace("</script>", "</scripttag>")
    ))));
</script>

Of course if you will need this in multiple place you can move this logic into a helper/extension method, like:

public static class JavaScriptExtensions
{
    public static string SerializeAndEscapeScriptTags(this object model)
    {
        return JsonConvert.SerializeObject(model,
            new JsonSerializerSettings()
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }).Replace("</script>", "</scripttag>");
    }
}

And use it with:

@using YourExtensionMethodsNamespace

<script type="text/javascript">
        $(document).ready(ko.applyBindings(new MyProfileVm(@Html.Raw(
            Model.SerializeAndEscapeScriptTags()))));
</script>

And on the JavaScript side in your Knockout viewmodel you need to replace back the </script> tag before the usage:

var MyProfileVm = function(data) {
   //...
   this.aboutMe = ko.observable(
     // you need  `"</scr"+ "ipt>"` because of the above mentioned problem.
   data.aboutMe.replace(/<\/scripttag>/g, "</scr"+ "ipt>"));
}

Of course you can also create a helper function for this, like:

function fixScriptTags(data) {
    for(var prop in data) {
        if (typeof(data[prop]) == "string") {
            data[prop] = data[prop].replace(/<\/scripttag>/g, "</scr"+ "ipt>");
        }
        //todo check for complex property values and call fixScriptTags recursively
    } 
    return data;
}

And use it with:

ko.applyBindings(new ViewModel(fixScriptTags(data)));

Demo JSFiddle.

于 2013-11-09T10:09:11.193 回答
1

I've had a similar problem, it came from using knockout.js to get input from a <textarea> just like you did. Everything was fine on the "create" part, but once I put the data back into an action via @Html.Raw(...), it turned out to contain linefeed and carriage-return characters that broke the json string. So I added something like this:

// Regex to replace all unescaped (single) backslashes in a string
private static Regex _regex = new Regex(@"(?<!\\)\\(?!\\)", RegexOptions.Compiled);

(I know it doesn't handle "\\\", but that doesn't appear from knockout)

Then I build my anonymous classes and do this:

var coJson = JsonHelper.Serialize(co);
var coJsonEsc = _regex.Replace(coJson, @"\\")

Maybe this can help you. I found it by breaking in the razor view and looking at the strings. This problem also appears with unesacped tabs (\t) and possibly other escape sequences.

于 2013-11-09T10:58:54.860 回答