views:

171

answers:

5

It turns out the following which looks like valid javascript, is not:

<html> 
<body>
<script>
 json = {test: "</script><script>alert('hello');</script>"};
</script>
</body>
</html>

The same text, when returned JSON via an ajax api works just as expected. However when rendered in-line results in a basic XSS issues.

Given an arbitrary correct JSON string, what do I need to do server side to make it safe for in-line rendering?

EDIT Ideally I would like the fix to work with the following string as well:

json = {test: "<\/script><script>alert('hello');<\/script>"};

Meaning, I have no idea how my underlying library is encoding the / char, it may have chosen to encode it, or it may have not. (so its likely a regex fix is more robust)

+2  A: 

In literal strings, put a backslash (\) before all “unsafe” characters, including the forward slash which occurs in “</script>” (/ → \/).

This would change your example to:

json = {test: "<\/script><script>alert(\"hello\");<\/script>"};

and it would still be valid JSON.

Of course you also have to escape the double-quote (" → \") and the backslash itself (\ → \\), but you would already have to do that anyway. You should also consider escaping the single-quote (' → \') to be on the safe side.

Timwi
so a simple replace("/", "\/") should do ? any other edge cases ?
Sam Saffron
@Sam Saffron: Yes, take care of double-quotes, single-quotes, and backslashes. See my edited answer.
Timwi
@Tim cool, yerp I already had those encoded, expanding my question with a slightly hairier sample.
Sam Saffron
There is nothing unsafe with the forward slash. The reason that this works is that it prevents the character combination `</`, which ends the script tag.
Guffa
@Guffa: I know. There is nothing unsafe with the forward slash *itself*, but there *is* something unsafe with the character sequence `</script>`, so escaping the slash makes it safe.
Timwi
+1  A: 

I found this list of characters to be escaped for JSON strings:

\b  Backspace (ascii code 08)
\f  Form feed (ascii code 0C)
\n  New line
\r  Carriage return
\t  Tab
\v  Vertical tab
\'  Apostrophe or single quote
\"  Double quote
\\  Backslash character

Using PHP? If so: json_encode

 echo json_encode("<\/script><script>alert(\"hello\");<\/script>");

Output:

 "<\\\/script><script>alert(\"hello\");<\\\/script>"

Another example:

 echo json_encode("</script><script>alert(\"hello\");</script>");

Output:

 "<\/script><script>alert(\"hello\");<\/script>"
Michael Robinson
Does that escape the forward slash? The help page doesn’t say. (In fact, it doesn’t say what *any* of the options mean.)
Timwi
Added example, looks like it does escape the forward slash :)
Michael Robinson
@Michael, can you expand on the algorithm I should use? I am not using PHP
Sam Saffron
Sorry about the confusion, Michael — I kept editing and deleting my comment. I should have paid more attention. But this is all irrelevant now that Sam has elucidated that he is not using PHP.
Timwi
+2  A: 

To start with, this is not JSON at all, it's a Javascript object. JSON is a text format that is based on the Javascript syntax.

You can either make sure that the code doesn't contain the </ character combination:

var obj = { test: "<"+"/script><script>alert(\"hello\");<"+"/script>" };

Or if you are using XHTML you can make sure that the content in the script tag is interpreted as plain data:

<script type="text/javascript">
//<![CDATA[
var obj = { test: "</script><script>alert(\"hello\");</script>" };
//]]>
</script>
Guffa
@Guffa, corrected the phrasing in the question, feel free to step in and further correct it. The "</" -> "<" + "/" feels a little iffy performance wise, the CDATA solution is really elegant
Sam Saffron
Actually thinking about it, a server side fix of `gsub("</","<\/")` should do the trick, no?
Sam Saffron
@Sam Saffron: Yes, using a backslash also works for preventing the `</` character combination. You can use a backslash before any character, even if the character has no special meaning that requires it to be escaped.
Guffa
All fair points in this answer, but @Sam Saffron, beware of the CDATA solution: it really *only* works if you’re *actually* using XHTML, which is very unlikely. Specifying an XHTML doctype is not enough. Read [Sending XHTML as text/html Considered Harmful](http://hixie.ch/advocacy/xhtml).
Timwi
@Guffa: If you’re already relying on actual XHTML being used, what’s the point in those extra “`//`”s before “`<![CDATA[`” and “`]]>`”? ☺
Timwi
A: 
sri
+2  A: 

One issue you might be running into is the fact that the HTML and javascript interpreters on the browser run interleaved.

<html> 
<body>
<script>
 json = {test: "</script><script>alert('hello');</script>"};
</script>
</body>
</html>

In your example, the HTML interpreter will give json = {test: " to the js interpreter and then it will find the next javascript block (delimited by <script> and </script> tags) and give alert('hello'); to the js interpreter. It doesn't matter that the </script> tag is in a javascript string, because the HTML interpreter is the one looking for js code blocks and doesn't understand js strings.

The first section will cause a js syntax error, while the second section will create the alert. I realize this doesn't answer your question of what to do, but perhaps it will shed more light on what is going on under the hood.

Ben Broussard