Embedding JavaScript Strings from an ASP.NET Page
I'm looking at a piece of code that's a custom control that embeds a bit of JavaScript into a page. Part of this JavaScript is generating some static string text directly into the page. I've been running this code for a while now as part of an application I'm working on with a customer.
But a couple of days ago I ran into a couple of problems with this control and as it turns out the problem is that the JavaScript strings embedded into the HTML stream aren't properly encoded. The code used is something like this (grossly simplified):
string markup = "Some Text"; string script = @" embedHtml("{0}"); function embedHtml(result) {{ alert(result) }}"; this.Page.ClientScript.RegisterStartupScript(typeof(Page), "embedHtml", string.Format(script,markup), true);
The idea is that the code gets some text that comes from the server side and gets embedded into the page. The client script basically takes the embedded string and displays it when the page loads (the real thing embeds a bunch of HTML into the page in dynamic positions but same idea).
Can you spot the problem???
Actually this is all fine and dandy with the code above. It works fine.
But it starts becoming a problem if the text that you are embedding contains special characters. Say the string that you embed contains carriage returns, extended characters or maybe more pertinently - double quotes (which is what blew my code up originally not surprisingly since the embedded string contained HTML).
For example take this C# string assignment on the server:
string markup = "Hello \"Rick\"\r\nRock On";
which when generated into the client side with the code above results in:
embedHtml("Hello "Rick"
Rock On");
which clearly is going to cause a JavaScript error when the page loads.
The problem is that using
embedHtml("{0}");
or
embedHtml('{0}');
is a string literal and it has to be embedded into the page properly or else code will blow up sporadically as certain characters are part of the strings embedded.
The fix for this is to encode the string to embed. The easiest way to use proper JavaScript string encoding is to use JSON encoding on the string and you can do that with the following code:
/// <summary> /// Encodes a string to be represented as a string literal. The format /// is essentially a JSON string. /// /// The string returned includes outer quotes /// Example Output: "Hello \"Rick\"!\r\nRock on" /// </summary> /// <param name="s"></param> /// <returns></returns> public static string EncodeJsString(string s) { StringBuilder sb = new StringBuilder(); sb.Append("\""); foreach (char c in s) { switch (c) { case '\"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; default: int i = (int)c; if (i < 32 || i > 127) { sb.AppendFormat("\\u{0:X04}", i); } else { sb.Append(c); } break; } } sb.Append("\""); return sb.ToString(); }
So now we can change the code to:
string markup = wwWebUtils.EncodeJsString("Hello \"Rick\"\r\nRock On"); string script = @" embedHtml({0}); function embedHtml(result) {{ alert(result) }}"; this.Page.ClientScript.RegisterStartupScript(typeof(Page), "embedHtml", string.Format(script,markup), true);
And voila - that works correctly. The embedded string in the JavaScript now looks like this:
"Hello \"Rick\"\r\nRock On"
Note that the embedHtml({0}) code has removed the quotes around the format/replace parameter as EncodeJsString will create the string with quotes around it so there's no ambiguity about which string delimiters to use. This can also reduce the complexity of code that requires nested string expressions.
This same logic applies if you use script expressions inside of a page:
alert( <%= wwWebUtils.EncodeJsString("My name is Sam\r\nRoll on") %> );
One place where I've actually used this a lot in the past is for client script localization. If you do something like this for example:
alert( <%= HttpContext.GetGlobalResourceObject("Resources","WarrantyDetail") %> );
you can run into the same encoding problems and this code should be changed to:
alert( <%= wwWebUtils.EncodeJsString(HttpContext.GetGlobalResourceObject("Resources","WarrantyDetail")) %> );
I know a lot of people truly disdain 'legacy' ASP classic script tags, but in some cases - localization especially - they are the easiest and most readable way to accomplish the task. Of course the same rules could be implied with a Label or Literal control and encoding the text explicitly in code.
I've run into this problem on a few occasions myself and I see it frequently in other people's code. While it may not be all that common to embed string literals into JavaScript when you do need to do it it's very important to encode the string.
It's these little details that are easy to miss when working with JavaScript so I'd thought I pass this one along... Hope this helps somebody out.
Other Posts you might also like
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Resolving Paths To Server Relative Paths in .NET Code
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Back to Basics: Rewriting a URL in ASP.NET Core
- Getting the ASP.NET Core Server Hosting Urls at Startup and in Requests
The Voices of Reason
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
I'm working on something similar, but want to add something for you. There are many HTML entities that will not render correctly in other browsers. For instance right double quote, left double quote, trademarks, etc. can all cause problems depending on the user's encoding settings. It's best to translate these from a literal TM character to the ™ entity. I have a function that parses a string and does such replacements - it would be easy to add to your switch() statement above.
Steve
# re: Embedding JavaScript Strings from an ASP.NET Page
Hmmm... I thought that JavaScript always uses UTF-8 formatting for strings, but then maybe that's just because I always force everything into UTF-8. Seems to be using anything but UTF-8 for page encoding would be a bad idea especially since ASP.NET makes that so easy from designer all the way through the Response Encoding.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
if (i < 32 || i > 127)
{
sb.AppendFormat("\\u{0:X04}", i);
}
else
{
sb.Append(c);
}
Why not use \\uXXX for characters 32 to 127 ??
Thanks in advance.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
you forgot:
case '\'':
sb.Append("\\\'");
break;
thx for sharing your code, and have a nice day :)
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
Because it would mean that your JSON size would bloat by 4x the size of the original content.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
So where is the ' a problem unencoded and does encoded really solve that problem if there is one?
# re: Embedding JavaScript Strings from an ASP.NET Page
Regardless, *you are indeed correct* and thank you for prompting me to look closer at my own code. The error of my ways was not changing the legacy code in place, but rather doctoring your code to suit. I realise now that your code above works just swell and my implemenatation of it was at fault. After many years of surrounding strings in single quotes (apostrophes) for javascript I had left them in place. No need. To illustrate, in a simplified version, what I had was:
ClientScript.RegisterStartupScript(typeof(string), "openFileError", "alert(\'" + Server.HtmlEncode(ex.Message) + "\');", true);
I removed the surrounding quotes from your method and replaced this with:
ClientScript.RegisterStartupScript(typeof(string), "openFileError", "alert(\'" + JavascriptHelper.EncodeJsString(ex.Message) + "\');", true);
Using the helper you have provided exactly as is, the *correct* implementation, after a new start and a fresh coffee is, of course:
ClientScript.RegisterStartupScript(typeof(string), "openFileError", "alert(" + JavascriptHelper.EncodeJsString(ex.Message) + ");", true);
I thank you again Rick, your implementation is correct. 'Error between computer and chair', as they say.
# re: Embedding JavaScript Strings from an ASP.NET Page
Glad it works as advertised... :-}
# re: Embedding JavaScript Strings from an ASP.NET Page
I was wondering if this could be used to encode CDATA closing tags and came up with the following code to extend your functionality:-
case ']':
sb.Append("\\]");
break;This should stop JavaScript enclosed in a CDATA tag having the CDATA closed by user input containing ]]>
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
Suppose you have the following code:-
string markup = wwWebUtils.EncodeJsString("Some text"); string script = @" embedHtml({0}); function embedHtml(result) {{ alert(result) }}";
This renders in ASP.NET 3.5 as:-
<script type="text/javascript"> //<![CDATA[ embedHtml("Some text"); function embedHtml(result) { alert(result) }//]]> </script>
All well and good. But suppose the text contains the ]]> sequence (end of a CDATA tag):-
string markup = wwWebUtils.EncodeJsString("Some text]]>");
This will cause the HTML to render as:-
<script type="text/javascript"> //<![CDATA[ embedHtml("Some text]]>"); function embedHtml(result) { alert(result) }//]]> </script>
Although the alert box displays as it should, this produces invalid XHTML because the ]]> sequence in the embedHtml function call closes the CDATA tag. The reason for my encoding of the ] character was to avoid the ]]> appearing as a literal in the produced code. This still decodes correctly in JavaScript as well as producing valid XHTML:-
<script type="text/javascript"> //<![CDATA[ embedHtml("Some text\]\]>"); function embedHtml(result) { alert(result) }//]]> </script>
I'd be interested in hearing your comments. As you say, it is best to avoid a million rules but if you didn't add ] encoding in this function then you'd have to add it somewhere else which would mean a double function call every time you wish to JS encode something. I await your reply with interest!
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
I wouldn't say its a bug in the browser as surely a CDATA tag denotes that a section shouldn't be parsed, therefore it shouldn't be looking for quotes either?
As you say it wouldn't be a common occurrence, I just noticed this was possible as ways of escaping a string can sometimes point to security vulnerabilities. But since JavaScript is client side I can't see any advantage gained by escaping a JavaScript string value anyway.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
Cheers.
# re: Embedding JavaScript Strings from an ASP.NET Page
Reflector gives me:
[NotRecommended("escape"), JSFunction(JSFunctionAttributeEnum.None, JSBuiltin.Global_escape)] public static string escape(object @string) { string str = Convert.ToString(@string); string str2 = "0123456789ABCDEF"; int length = str.Length; StringBuilder builder = new StringBuilder(length * 2); int num3 = -1; while (++num3 < length) { char ch = str[num3]; int num2 = ch; if ((((0x41 > num2) || (num2 > 90)) && ((0x61 > num2) || (num2 > 0x7a))) && ((0x30 > num2) || (num2 > 0x39))) { switch (ch) { case '@': case '*': case '_': case '+': case '-': case '.': case '/': goto Label_0125; } builder.Append('%'); if (num2 < 0x100) { builder.Append(str2[num2 / 0x10]); ch = str2[num2 % 0x10]; } else { builder.Append('u'); builder.Append(str2[(num2 >> 12) % 0x10]); builder.Append(str2[(num2 >> 8) % 0x10]); builder.Append(str2[(num2 >> 4) % 0x10]); ch = str2[num2 % 0x10]; } } Label_0125: builder.Append(ch); } return builder.ToString(); }
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
I did have to add encoding of single quotes for my use - writing out JavaScript events to be called when clicking on a link, such as:
<a href='#' onclick='selectemployee("O\u0027Hanlon, Fred","fred.ohanlon@example.com");'>O'Hanlon, Fred</a>
The parameters were supplied from variables (in a loop) when building up the html, and so could contain anything.
Without the encoding of the single quote, the bare quote will prematurely end the onclick event.
You need to encode it as \u0027 rather than \\\' as that still prematurely ends the event code.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
thx
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
Like : Reputation Point : 537456
Can be more readable if we can write it as Reputation Point : 537,456
ASP.NET provide features like String.Format(format,arg0) to format arguments into different forms.
For above solution you can implement
Response.Write(String.Format("{0:#,###}", 123456789));
Which will print 123,456,789
{0:#,###} → Known as the format string where “{ ,}”are compulsory to mentation.
The first part before ':' represent the argument number & it will be an integer.
The second part after ':' represent the format that you want your argument to be converted.
http://www.mindfiresolutions.com/Formating-the-string-before-displaying-in-ASPNET-887.php
# re: Embedding JavaScript Strings from an ASP.NET Page
case '\'':
sb.Append("\\\'");
break;
# re: Embedding JavaScript Strings from an ASP.NET Page
I also agree that escaping single quotes is necessary. Especially when the function is called EncodeJsString and not EncodeJSONStringAndAddAnOuterDelimiter :-)
Anybody has a tip how to escape strings used in JS in events?
when I escape a text containing both quotes I get this which is still not OK.
some ' text " with both quotes
<a onclick="edit('some \' text \" with both quotes ');" >
thanks
Dan
# re: Embedding JavaScript Strings from an ASP.NET Page
case '\"':
//sb.Append("\\\"");
sb.Append(""");
break;
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
Embedding a string like this won't work:
string embed = "some text with script. <script type=\"text/javascript\">alert('do something');</script>";
When you embed this string in javascript a bad thing happens:
<script type="text/javascript">
document.write(<%=wwWebUtils.EncodeJsString(embed)%>);
</script>
Which renders this:
<script type="text/javascript"> document.write("some text with script. <script type=\"text/javascript\">alert('do something');</script>"); </script>
The document.write will not execute and a script error is generated because the browser interprets the "</script>" at the end of the string as the actual end of the outer script.
A possible solution is to encode the less than sign by adding an extra case to the EncodeJsString method:
case '<': sb.Append("\\x3c"); break;
Hope this helps.
-Bryan
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
It is "free to use, modify, sell", or whatever you feel like.
It addresses a number of the issues:
1) </script> will not break the enclosing <script>
2) ]]> will not break this in CDATA sections
3) Will not add any outside quotes
4) Will pass harmlessly through XML (& is encoded as well)
It is meant to encode literals to Javascript Strings and nothing else. Do not use for URL encoding. Do not use for JSON.
public class JsEncoder
{
static Regex EncodeLiteralRegex;
// Given a string, return a string suitable for safe
// use within a Javascript literal inside a <script> block.
// This approach errs on the side of escaping.
public static string EncodeLiteral (string value)
{
if (EncodeLiteralRegex == null) {
// initial accept "space to ~" in ASCII then reject quotes
// and some XML chars (this avoids `</script>`, `<![CDATA[..]]>>`, and XML vs HTML issues)
// this allows / non-escaped, which is against JSON
var accepted = Enumerable.Range(32, 127 - 32)
.Except(new int[] { '"', '\'', '\\', '&', '<', '>' });
// pattern matches everything but accepted
EncodeLiteralRegex = new Regex("[^" +
string.Join("", accepted.Select(c => @"\x" + c.ToString("x2")).ToArray())
+ "]");
}
return EncodeLiteralRegex.Replace(value ?? "", (match) =>
{
var ch = (int)match.Value[0]; // only matches a character at a time
switch (ch) {
case '"': return @"\""";
case '\'': return @"\'";
case '\\': return @"\\";
case '\b': return @"\b";
case '\f': return @"\f";
case '\n': return @"\n";
case '\r': return @"\r";
case '\t': return @"\t";
default:
return ch <= 127
? @"\x" + ch.ToString("x2") // not JSON
: @"\u" + ch.ToString("x4");
}
});
}
}
# re: Embedding JavaScript Strings from an ASP.NET Page
instead of
sb.Append("\""")
Next
should be
Next
sb.Append("\""")
# re: Embedding JavaScript Strings from an ASP.NET Page
Also worth looking at .NET Framework 4.0's HttpUtility.JavaScriptStringEncode()
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
I was having an issue with single quotes. My text consisted of html tags as well as normal text.
I am using Asp.net C sharp.
I defined the hyperlink tag from code behind.
The onclick event's handler had to be enclosed in single quotes ('') otherwise the tags in the parameters were interpreted as the end of the hyperlink tag.
<a href='javascript:void(0);' onclick='test(" + a + "," + encoded_b + "," + encoded_c + ")'" + ">Click Here</a>
Now any single quotes in the text produced errors.
So i encoded the single quote character using:
case '\'': sb.Append("'"); break;
I am a noob , I don't know much about JSON, your code helped me a lot, thanks again.
# re: Embedding JavaScript Strings from an ASP.NET Page
For URL, I use Server.UrlEncode
For JSON, I use Json.Encode
Since these are part of ASP.Net Web Pages (http://www.asp.net/web-pages/overview/more-resources/asp-net-web-pages-api-reference)
Thanks also to Rick Strahl for providing a custom JsonEncoder class and relevant discussions here which I found educational.
# re: Embedding JavaScript Strings from an ASP.NET Page
This code is vulnerable to Cross Site Scripting (XSS) and I managed to succeed in generating an attack vector.
The code does not handle </script> tags in the JavaScript code. Such tags will cause the browser to think the script tag is over, leaving the interpretation inside an HTML context but without proper encoding.
See the answer here for more information: http://stackoverflow.com/a/28716574/413180
Info regarding XSS in general: https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
See https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet for comprehensive details on how to prevent this.
Or just use the new HttpUtility.JavaScriptStringEncode .NET function rather than the code on this blog post.
# re: Embedding JavaScript Strings from an ASP.NET Page
# re: Embedding JavaScript Strings from an ASP.NET Page
The OWASP guide recommends to escape all characters less than 256 (rather than less than 32 or above 127 like you have there). This is to be "extra safe" and to guard against anything that might be able to get round angle bracket encoding in future.
Any chance you could put a message on your post to inform Googlers about the new way (JavaScriptStringEncode function)?
# re: Embedding JavaScript Strings from an ASP.NET Page
I still don't know how this code is vulnerable to Cross Site Scripting (XSS). Can we use it in Rails as well or in rails it would be different
# re: Embedding JavaScript Strings from an ASP.NET Page
Is there an equivalent method to JavaScriptStringEncode() in the new AspNetCore packages? I need this functionality but want to avoid using old AspNet System.Web dependencies.
# re: Embedding JavaScript Strings from an ASP.NET Page
Yes - in the Westwind.AspNetCore package and the WebUtils class: https://github.com/RickStrahl/Westwind.AspNetCore/blob/master/Westwind.AspNetCore/Utilities/WebUtils.cs#L26
# re: Embedding JavaScript Strings from an ASP.NET Page