4

I have a simple Java object that I'm using Jackson to serialize to JSON, and then I'm dropping that into a <script> tag in my JSP page as part of initializing a JavaScript object. e.g.

<script>SomeLib.load(${someObject});</script>

This works great unless one of the fields of someObject is a String that contains "</script>", because of this issue. That is, if the output looks like this:

<script>SomeLib.load({"someValue":"hacked!</script>"});</script>

then the browser (tested in both Chrome and FF so far) believes the </script> tag after hacked! is closing the script tag. Which breaks the JavaScript and leaves "});</script> visible to the user.

Is there a way to get Jackson to escape that value in some fashion that will fix this problem?

Community
  • 1
  • 1
jwl
  • 10,268
  • 14
  • 53
  • 91
  • 1
    Just escape the xml, you'd need a library for that unless you can use jstl, but since it looks like you are using a sort of tag library, just use the jstl function library's escapexml, OR the core jstl's library "out" which escapes xml by default – Zachary Craig Feb 02 '15 at 23:32
  • @zack6849 that's not quite right because `escapeXml="true"` will also escape the quotes, you end up with `{"someValue":"hacked!"}` – jwl Feb 03 '15 at 00:06
  • Escaping the forward slash `/` is common pattern I often see to counter this kind of "attack" http://stackoverflow.com/questions/28288322/how-to-get-jackson-to-escape-a-script-in-output-string – pozs Feb 09 '15 at 15:00

2 Answers2

4

Ravis answer is wrong. See my comment there.

A better way is to escape the slash in </ with a backslash, like:

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
...
<script>SomeLib.load(${fn:replace(jsonString, "</", "<\\/")});</script>

Note: two backslashes are needed so the first one is escaped in the JSP syntax.

The good part is, it needs no special decoding.

PS: To do it on Jackson side, see this blog - it covers escaping other characters, but just add slash to the list to escape (all) slash characters.

Community
  • 1
  • 1
David Balažic
  • 1,319
  • 1
  • 23
  • 50
0

You can use the JSTL function fn:replace() to escape just the starting < with &lt; which would effectively prevent any tag in your JSON string from getting interpreted as HTML.

So, this

<script>SomeLib.load(${fn:replace(jsonString, "<", "&lt;")});</script>

would get rendered fine in a browser as

<script>SomeLib.load({"someValue":"hacked!&lt;/script>"});</script>

But, before you can use replace() you'll need to import the JSTL functions tag library as

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

No, that won't work because the < will literally be in that JavaScript string (it is not unescaped).

That's the whole point because if it comes out as <, it would break the HTML parser/browser.

You need to remember that when a protocol or application encodes something (like a URL string encoded in UTF-8 by a browser) it needs to be decoded as well prior to being used (like the web server does with the URL at server-side) or the functionality could break.

So, now that you know that your JSON string comes semi-encoded, you would need to modify the consumer as well; either the SomeLib.load() method to replace &lt; back with a <, or if that's a third-party library, decode its input in Javascript itself.

<script>
  var json = '${fn:replace(jsonString, "<", "&lt;")}';
  SomeLib.load(JSON.parse(json.replace("&lt;", "<")));
</script>
Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
  • 1
    No, that won't work because the < will literally be in that JavaScript string (it is not unescaped). http://jsbin.com/kemeqagare – jwl Feb 07 '15 at 20:46
  • This is wrong. If the original data contains "<" then your code will mangle it to "<". – David Balažic Feb 10 '15 at 17:15