Escaping single quote from an MVC 3 Razor View variable - javascript

I have a variable within item.Name that contains the string "It's Tuesday!".
To alleviate javascript errors, in the c# controller I have already escaped this single quote.
On the webpage, it appears like this "It\'s Tuesday!".
This at least prevents any javascript errors, however, I do not want the actual string displayed to contain the backslash that has escaped it.
How might I be able to revert the escaping once the javascript errors have already been taken care of? This feels like a rather simple problem, but I am a bit unfamiliar with MVC 3. Any hint is much appreciated! My searching did not find me any example specific to this.
An example of my code in the Razor View:
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item.Name) // item.Name = "It\'s Tuesday!"
}

Create an extension method you can use or simply encode directly. I would not encode prior to the view getting it unless you have a separate property in your viewmodel meant for this (not a bad idea()
This is not tested but something along these lines
public static class HtmlExtensions
{
public static IHtmlString JavaScriptEncode(this HtmlHelper html, string item)
{
return new HtmlString(HttpUtility.JavaScriptStringEncode(item));
}
}
You can then just call #Html.JavaScriptEncode(yourProperty) from the view or simply reference your encoded property name.

The following can be used to prevent encoding of the output again. This does however rely on you properly dealing with it yourself.
MVCHTMLString
#MvcHtmlString.Create(yourString)

I prefer to make my views dumb. Therefore, I'd have a property called something like 'Display' or 'DisplayName' which handled the escaping for the view.

Related

Extension method to escape variable for javascript?

public string GetEscapedTitle() { return Title.Replace("'", "\\'").Replace("\"", "\\\""); }
I created the above to deal with issues I ran into when doing this in the view:
<a onclick="AddToMyList('#Model.Title', ...
That seemed to fix the errors I was getting when the Title contained single or double quotes. But this wasn't the only property I had the issue with so I had this brilliant idea to create an extension method like so:
public static string EscapeQuotes(this string s)
{
return s.Replace("'", "\'").Replace("\"", "\\\"");
}
Unfortunately, this put me back to square one and I had the same error I got in the beginning when I used it. So what's the difference?
The only thing I can think of is that maybe with the GetEscapedTitle() method razor encodes it after it's escaped but maybe with the extension method it still somehow gets encoded before the extension method is run? That doesn't make sense to me but it's all I can think of right now for why it wouldn't work. :(
Edit: FYI I also just tried adding the .Replace() calls directly in the razor code and that works perfectly fine. But for some strange reason if I do it in the extension method it won't work.

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

I use the Newtonsoft library to convert C# objects into JSON. Is this use of Newtonsoft.Json.JsonConvert.SerializeObject secure, or is additional encoding necessary? If additional encoding is needed, what do you suggest?
Here is how I use it in a Razor view:
<script type="text/javascript">
var jsModel = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model))
</script>
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.
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-json attribute value from the the-div element and JSON.parse it. The benefit to this is that you don't need to worry about which characters require special encoding. The SerializeObject method guarantees that the JSON blob is well-formed, and the # operator guarantees that 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.
Using #Html.Raw alone 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:
var jsModel = JSON.parse("#Html.Raw(HttpUtility.JavaScriptStringEncode(
JsonConvert.SerializeObject(Model)
))");
I used the following very simple test. If I were only using #Html.Raw like in the question the "Bad" alert appears. Wrapped up in this way, I have valid JavaScript and the alert does not appear.
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.
I made this JsonConverter that encodes all strings with Microsoft Web Protection Library-library (aka 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>
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.
Thought to drop a line of code or two based on Torbjörn Hansson's golden answer:
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 " ' ' )"> 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.

Does knockout 'attr' always assume input require escape, is it possible to turn it off?

If one were to bind attr: { href: url }, does it always escape url according to rfc2396?
I have recently encounter issue where & in json response is converted to &amp; by knockout, I am guessing knockout has no way of knowing it's double-encoded (html then json), and thus convert any & into entities.
Should we always give raw url to attr binding, but not for html binding? what's the rule of thumb here?
Try this:
JSON.stringify('"some text"')
KnockoutJS postJSON method escapes my string why?

Access to model object by EL in javascript function?

Here is my markup, I am using jtsl core tag
<c:forEach var="attr" items="${attributes}">
<c:when test='${attr.controlType == "textField"}'>
<script>createTextField("${attr}");</script>
</c:when>
</c:forEach>
So the "attributes" is a list of objects which resides in the model.
I want to call the createTextField function and I want access to the "attr"
in that function.
Here is my function, but I can't get access to the object, says it is undefined.
function createTextField(object) {
document.write(object.name);
}
Any ideas? would be appreciated.
This is not going to work. Java and JavaScript doesn't run in the same environment. You're basically passing attr.toString() to the JavaScript function which look by default like com.example.ClassName#hashcode. The JavaScript String object doesn't have a name property.
There are basically two ways to get it to work:
You need to convert the Java object as represented by #{attr} to a string which conforms the JavaScript object notation (also known as JSON). E.g.
<script>createTextField(${attr.asJson});</script>
with something like (with little help of Gson):
public String getAsJson() {
return new Gson().toJson(this);
}
You can of course also manually build it using StringBuilder or something. JSON format isn't that hard.
Pass only the property/properties of interest as long as they can be represented as String. E.g.
<script>createTextField("${attr.name}");</script>
with
function createTextField(name) {
document.write(name);
}

Is there a good alternative to having JavaScript code as string literals in my ASP.net code?

Working on an ASP.net web application, I've been wondering if there is a good way to avoid writing JavaScript code in string literals in my ASP.net code. See here: https://web.archive.org/web/20211020150655/https://www.4guysfromrolla.com/articles/030202-1.aspx.
In the linked example, I see code that looks like:
Private Sub Calendar1_SelectionChanged(sender As Object, e As EventArgs)
Dim strjscript as string = "<script language=""javascript"">"
strjscript &= "window.opener." & _
Httpcontext.Current.Request.Querystring("formname") & ".value = '" & _
Calendar1.SelectedDate & "';window.close();"
strjscript = strjscript & "</script" & ">" 'Don't Ask, Tool Bug
Literal1.Text = strjscript 'Set the literal control's text to the JScript code
End Sub
I'm not used to using much JavaScript. A lot of the code that I've worked with has been mostly server-side coding with T-SQL. The above code gives me a headache just looking at it. Not only is it ugly, but it shows a pattern where a malicious user could try to inject malicious code.
Is there a better way to avoid manipulating JavaScript code as string literals? Think of the ways we have to avoid manipulating T-SQL code as string literals.
Ugh, dynamically building javascript and putting it inside a literal?
Generally the only time I embed javascript in code is when I am making a custom control and want it packaged neatly (no sepatate js file to worry about), and even then I use RegisterClientScriptBlock instead of a hack like this.
Why not just have a javascript function inside the page source (or an include file) that takes two parameters (form name and selected date) and then dynamically build the function call instead of the entire script?
A common way is to use the clientscriptmanager class:
http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
You can call the registerstartupscript method, which will add the script to the end of your page, executes when the page finishes loading but before the page's OnLoad event is raised.
The RegisterClientScriptBlock method adds the script to the top of your page. This is where you might add commonly used fnctions.
Dim script As New StringBuilder()
script.AppendFormat("window.opener.{0}", Httpcontext.Current.Request.Querystring("formname"))
script.AppendFormat(".value = '{0}';window.close();", Calendar1.SelectedDate)
Dim cs As ClientScriptManager = Page.ClientScript
cs.RegisterClientScriptBlock(Me.GetType(), "ScriptKey", script.ToString(), true)
The last parameter tells the script manager to wrap the script in <script>...</script> tags so that you don't have to.
Also, if you are adding scripts from a user control, the "ScriptKey" makes sure that the same script does not get added more than once. If you need a separate script for each control, you can dynamically generate that parameter based on the control id.
The other common method for adding links to script files on your page is RegisterClientScriptInclude
Instead of writing out the complete function, embed the function on the page or in an external file and only dynamically write out the values. For example:
<script>
<asp:Literal ID="ScriptValues" runat="server" />
</script>
<script>
function foo(bar) { ... }
</script>
Then in your code behind or wherever (sorry, I don't do VB):
var values = new StringBuilder();
values.Append("var bar = " + bar + ";");
...
ScriptValues.Text = values.ToString();
for starters the StringBuilder is far better for this than using String (it's easier to read and more performance tuned)
Dim sbuild As StringBuilder = New StringBuilder
sbuild.Append("<script language=""javascript"">")
sbuild.Append("window.opener.")
sbuild.Append(Httpcontext.Current.Request.Querystring("formname"))
sbuild.Append(".value = ")
sbuild.Append(Calendar1.SelectedDate)
sbuild.Append("';window.close();")
sbuild.Append("</script>")
Literal1.Text = sbuild.ToString
But beyond that, I would suggest trying something like the TagBuilder Class. It says it's for MVC, but I don't see why you can't use it in a Web Forms scenario as well (you'd just have to import the MVC namespace) - (though I could be wrong on this part).
There are a few things to consider in dealing with your issue.
There are several methods, including Page.RegisterClientScript, that handle some of the dirty work, by properly wrapping your JavaScript code in the proper tags, as well as placing it within the proper place in the page (inline vs. beginning/end) that will deal with some of the formatting issues.
Your code sample above is VB.Net, which is little rough working with with large amounts of text due to the requirement of having to append the &_ to every line. C# does a better job at this. The good news is that with the release of .Net 4, you no longer have to worry about doing all the line concatenations.
If you are dealing with a large amount of text that you need to embed, you could consider keeping your JavaScript in a separate text file, and read the file into your literal, or script registration. You can even do some simple string replacements if you have to have some dynamic data. The StringBuilder class is also a help, with the use of the Append and AppenLine methods(), but again it depends on how much text you're dealing with and how often you'll be needing to work with the code block in question.
Move as much as possible into a .js file.
If anything, you should only need to render simple js function calls. I try to minimize the need for these, by adding a css class and then using jquery's class selector to attach the behavior.
From the example posted by rockinthesixstring, if you want to "clean" up the visual aspect of the code, you would also write it as:
Dim sbuild As StringBuilder = New StringBuilder
With sbuild
.Append("<script language=""javascript"">")
.Append("window.opener.")
.Append(Httpcontext.Current.Request.Querystring("formname"))
.Append(".value = ")
.Append(Calendar1.SelectedDate)
.Append("';window.close();")
.Append("</script>")
End With
Literal1.Text = sbuild.ToString
However I would look into the methods Page.ClientScript
Page.ClientScript.RegisterStartupScript
or if you are using a ScriptManager
ScriptManager.RegisterStartupScript
When working with JavaScript in ASP.NET, these are the paths you should follow:
Put it in a seperate JavaScript file. More maintanable.
However, if you (for whatever reason) can't put it in a JavaScript file, put it in a static class which exposes the script as constants with placeholders for value insertion (use the # symbol so you don't have to escape characters.
Like this:
public static class JavaScriptStuff
{
public const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
}
Then register it using ClientScriptManager - this way you also don't need to explicity open/close the script tag (stops human error).
http://msdn.microsoft.com/en-us/library/system.web.ui.scriptmanager.aspx
string myScript = string.Format(JavaScriptStuff.SpecialScriptFormat, HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate);
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", myScript, true);
You can go even further, and not expose the scripts as public properties, instead expose "getter" methods which accept the params - which adds another layer of maintainability:
public static class JavaScriptStuff
{
private const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
public string GetSpecialScript(string queryString, string selectedDate)
{
return string.Format(SpecialScriptFormat, queryString, selectedDate);
}
}
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", JavaScriptStuff.GetSpecialScript(HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate), true);
HTH

Categories

Resources