69

Maybe I'm just thinking about this too hard, but I'm having a problem figuring out what escaping to use on a string in some JavaScript code inside a link's onClick handler. Example:

<a href="#" onclick="SelectSurveyItem('<%itemid%>', '<%itemname%>'); return false;">Select</a>

The <%itemid%> and <%itemname%> are where template substitution occurs. My problem is that the item name can contain any character, including single and double quotes. Currently, if it contains single quotes it breaks the JavaScript code.

My first thought was to use the template language's function to JavaScript-escape the item name, which just escapes the quotes. That will not fix the case of the string containing double quotes which breaks the HTML of the link. How is this problem normally addressed? Do I need to HTML-escape the entire onClick handler?

If so, that would look really strange since the template language's escape function for that would also HTMLify the parentheses, quotes, and semicolons...

This link is being generated for every result in a search results page, so creating a separate method inside a JavaScript tag is not possible, because I'd need to generate one per result.

Also, I'm using a templating engine that was home-grown at the company I work for, so toolkit-specific solutions will be of no use to me.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rmeador
  • 25,504
  • 18
  • 62
  • 103

13 Answers13

80

In JavaScript you can encode single quotes as "\x27" and double quotes as "\x22". Therefore, with this method you can, once you're inside the (double or single) quotes of a JavaScript string literal, use the \x27 \x22 with impunity without fear of any embedded quotes "breaking out" of your string.

\xXX is for chars < 127, and \uXXXX for Unicode, so armed with this knowledge you can create a robust JSEncode function for all characters that are out of the usual whitelist.

For example,

<a href="#" onclick="SelectSurveyItem('<% JSEncode(itemid) %>', '<% JSEncode(itemname) %>'); return false;">Select</a>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Duncan Smart
  • 31,172
  • 10
  • 68
  • 70
23

Depending on the server-side language, you could use one of these:

.NET 4.0

string result = System.Web.HttpUtility.JavaScriptStringEncode("jsString")

Java

import org.apache.commons.lang.StringEscapeUtils;
...

String result = StringEscapeUtils.escapeJavaScript(jsString);

Python

import json
result = json.dumps(jsString)

PHP

$result = strtr($jsString, array('\\' => '\\\\', "'" => "\\'", '"' => '\\"', 
                                 "\r" => '\\r', "\n" => '\\n' ));

Ruby on Rails

<%= escape_javascript(jsString) %>
Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
  • For .NET, your answer appears to be the only way. `btn.OnClientClick = "return confirm('Are you sure?')";` is valid, but the only way to put a single quote in there (e.g. `"return confirm('You're sure, right?');"` is to encode it...neither backslash nor `'` work. – John Riehl Jun 07 '16 at 20:39
11

Use hidden spans, one each for each of the parameters <%itemid%> and <%itemname%> and write their values inside them.

For example, the span for <%itemid%> would look like <span id='itemid' style='display:none'><%itemid%></span> and in the javascript function SelectSurveyItem to pick the arguments from these spans' innerHTML.

Shyam Kumar Sundarakumar
  • 5,649
  • 13
  • 42
  • 69
  • 2
    This is really the best answer. If you place the values in and later get them with jQuery $('#itemid').val() there is no need for special treatment. – Sire Dec 19 '10 at 19:41
9

If it's going into an HTML attribute, you'll need to both HTML-encode (as a minimum: > to &gt; < to &lt and " to &quot;) it, and escape single-quotes (with a backslash) so they don't interfere with your javascript quoting.

Best way to do it is with your templating system (extending it, if necessary), but you could simply make a couple of escaping/encoding functions and wrap them both around any data that's going in there.

And yes, it's perfectly valid (correct, even) to HTML-escape the entire contents of your HTML attributes, even if they contain javascript.

Dan
  • 61,568
  • 9
  • 61
  • 78
9

Try avoid using string-literals in your HTML and use JavaScript to bind JavaScript events.

Also, avoid 'href=#' unless you really know what you're doing. It breaks so much usability for compulsive middleclickers (tab opener).

<a id="tehbutton" href="somewhereToGoWithoutWorkingJavascript.com">Select</a>

My JavaScript library of choice just happens to be jQuery:

<script type="text/javascript">//<!-- <![CDATA[
jQuery(function($){
   $("#tehbutton").click(function(){
        SelectSurveyItem('<%itemid%>', '<%itemname%>');
        return false;
   });
});
//]]>--></script>

If you happen to be rendering a list of links like that, you may want to do this:

<a id="link_1" href="foo">Bar</a>
<a id="link_2" href="foo2">Baz</a>

<script type="text/javascript">
   jQuery(function($){
        var l = [[1,'Bar'],[2,'Baz']];
        $(l).each(function(k,v){
           $("#link_" + v[0] ).click(function(){
                SelectSurveyItem(v[0],v[1]);
                return false;
           });
        });
   });
 </script>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
4

Another interesting solution might be to do this:

<a href="#" itemid="<%itemid%>" itemname="<%itemname%>" onclick="SelectSurveyItem(this.itemid, this.itemname); return false;">Select</a>

Then you can use a standard HTML-encoding on both the variables, without having to worry about the extra complication of the javascript quoting.

Yes, this does create HTML that is strictly invalid. However, it is a valid technique, and all modern browsers support it.

If it was my, I'd probably go with my first suggestion, and ensure the values are HTML-encoded and have single-quotes escaped.

Dan
  • 61,568
  • 9
  • 61
  • 78
  • personally I think that technique is a bad way to associate data with nodes. http://noteslog.com/metaobjects/ is a slightly advanced technique, but it leaves you with a valid page and results in the same data. – Kent Fredric Sep 18 '08 at 22:48
  • I completely agree that it's a little bit of a filthy solution, but it can certainly make some tricky things significantly easier. It's definitely a niche fix, though ;) The 'metaobject' technique is an interesting one, although a bit hacky in itself, I can see how it would be useful. – Dan Sep 18 '08 at 22:56
1

Any good templating engine worth its salt will have an "escape quotes" function. Ours (also home-grown, where I work) also has a function to escape quotes for javascript. In both cases, the template variable is then just appended with _esc or _js_esc, depending on which you want. You should never output user-generated content to a browser that hasn't been escaped, IMHO.

livingtech
  • 3,570
  • 29
  • 42
1

I have faced this problem as well. I made a script to convert single quotes into escaped double quotes that won't break the HTML.

function noQuote(text)
{
    var newtext = "";
    for (var i = 0; i < text.length; i++) {
        if (text[i] == "'") {
            newtext += "\"";
        }
        else {
            newtext += text[i];
        }
    }
    return newtext;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

Declare separate functions in the <head> section and invoke those in your onClick method. If you have lots you could use a naming scheme that numbers them, or pass an integer in in your onClicks and have a big fat switch statement in the function.

moonshadow
  • 86,889
  • 7
  • 82
  • 122
1

Use the Microsoft Anti-XSS library which includes a JavaScript encode.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
blowdart
  • 55,577
  • 12
  • 114
  • 149
1

First, it would be simpler if the onclick handler was set this way:

<a id="someLinkId"href="#">Select</a>
<script type="text/javascript">
  document.getElementById("someLinkId").onClick = 
   function() {
      SelectSurveyItem('<%itemid%>', '<%itemname%>'); return false;
    };

</script>

Then itemid and itemname need to be escaped for JavaScript (that is, " becomes \", etc.).

If you are using Java on the server side, you might take a look at the class StringEscapeUtils from jakarta's common-lang. Otherwise, it should not take too long to write your own 'escapeJavascript' method.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexandre Victoor
  • 3,104
  • 2
  • 27
  • 27
0

Is the answers here that you can't escape quotes using JavaScript and that you need to start with escaped strings.

Therefore. There's no way of JavaScript being able to handle the string 'Marge said "I'd look that was" to Peter' and you need your data be cleaned before offering it to the script?

Steve Perks
  • 5,490
  • 3
  • 28
  • 41
0

I faced the same problem, and I solved it in a tricky way. First make global variables, v1, v2, and v3. And in the onclick, send an indicator, 1, 2, or 3 and in the function check for 1, 2, 3 to put the v1, v2, and v3 like:

onclick="myfun(1)"
onclick="myfun(2)"
onclick="myfun(3)"

function myfun(var)
{
    if (var ==1)
        alert(v1);

    if (var ==2)
        alert(v2);

    if (var ==3)
        alert(v3);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131