2

I ran across an odd notation in the jsfiddle.net generated pages

<script type='text/javascript'>//<![CDATA[ 
$(window).load(function(){
$('form, table').animate({
    opacity: 1
});
});//]]>  

</script>

I sometimes feed JSON data directly into a script. I personally do not use CDATA notation, but I am wanting to confirm whether an attacker could inject CDATA notation and cause bad things to happen.

I run the objects through a standard JSON stringifier which follows all the rules, then I replace </script with <\/script, not case sensitive. Is this sufficient?

2 Answers 2

6

The //<![CDATA[ hack is used in XHTML pages that have to parse as both HTML and XML.

In the HTML parsing rules, <script> and <style> are special “CDATA elements”, whose contents up to the next </ sequence (HTML4) or </script sequence (HTML5) are raw data, so if (x<y) can be written without any encoding; this would foul up XML parsers.

In the XML parsing rules there are no special elements and so the same statement would have to be written as if (x&lt;y); this would foul up HTML parsers.

In order to allow < to be written unescaped and mean the same thing to both XML and HTML parsers, you can wrap the script in an XML CDATA Section, then protect HTML parsers from that odd construct by hiding it in a JavaScript comment.

If you are using this construct to allow you to include these characters in a string literal without escaping, it's not enough, because you still have to escape away both the sequence </ (for HTML) and ]]> (for XML). One way of escaping those sequences in a JS string literal is to always encode <>& characters to \x3C, \x3E and \x26 respectively... in which case you would no longer need the CDATA Section.

I run the objects through a standard JSON stringifier which follows all the rules, then I replace </script with <\/script, not case sensitive. Is this sufficient?

Not necessarily.

  1. HTML syntax. <\/script is fine for HTML but not XHTML, as above.

  2. JavaScript syntax. There are—due to an unfortunate oversight in the design of JSON—some Unicode control characters that are valid in JSON but not valid in JavaScript.

Most notably the characters U+2028 and U+2029, Line and Paragraph Separator, which act as newlines. Injecting a newline into the middle of a string literal will most likely cause a syntax error (unterminated string literal).

There are more control characters that are supposed to be invalid in JS string literals, but which don't actually break browsers in practice.

If your JSON encoder habitually encodes all non-ASCII characters this won't be a problem.

Alternative to getting embedded JS-encoding right: avoid inline scripts completely, put your data in the HTML page (where normal HTML-escaping rules apply) and retrieve it from linked scripts using DOM methods.

6
  • In response to If you are using this construct: As mentioned above I personally do not use CDATA notation, but I am wanting to confirm whether an attacker could inject CDATA notation. Jan 26, 2012 at 22:49
  • In response to number 2, I think it would be an oversight in implementation. The graphical spec actually mentions unicode control characters as needing to be escaped. Your point is still worth mentioning though. Jan 26, 2012 at 22:51
  • 2
    @George: I don't see where it requires non-ASCII to be escaped; it only says they can be. Indeed, many JSON encoders don't \u-encode, as it results in longer output. The production for string is listed as including “any Unicode character (except `, "` and control characters)”; arguably ‘control characters’ could include characters like U+2028, but in RFC 4627 which defines JSON more formally, it doesn't.
    – bobince
    Jan 27, 2012 at 8:34
  • 1
    As for CDATA Sections, you would already have a well-formedness problem if the user can inject either < or ]]> into an unprotected script block in an XHTML document (]]> is actually disallowed even outside of CDATA sections, an unusual and little-known requirement). So CDATA isn't specifically a new problem there, you should be \x-escaping <>& in any case. If you are working with a non-XML HTML parent document, you don't have that concern and replacing </ is enough.
    – bobince
    Jan 27, 2012 at 8:38
  • 1
    Right. When writing a JSON encoder it is prudent to escape at least \u003C, \u003E, \u0026, \u2028 and \u2029 in addition to the C0 (ASCII) and C1 control characters, to account for the likely case of someone embedding your output in a <script> block, even though this is not part of JSON itself.
    – bobince
    Jan 27, 2012 at 20:37
3

Short answer, replacing the characters < and > globally with \u003c and \u003e will allow embedding in HTML <script> elements safely, and can be performed on valid JSON without breaking it.

To embed it in XML, you need to make sure the JSON only contains characters that can appear in XML. Specifically, XML can only contain non-control characters so you need to also \u.... escape any such characters.


http://code.google.com/p/json-sanitizer/ takes JSON-like input and produces an output that is valid JSON and that is safe to embed in HTML <script> elements and inside XML <![CDATA[...]]> sections.

Given JSON-like content, converts it to valid JSON.

This can be attached at either end of a data-pipeline to help satisfy Postel's principle:

be conservative in what you do, be liberal in what you accept from others

Applied to JSON-like content from others, it will produce well-formed JSON that should satisfy any parser you use.

Applied to your output before you send, it will coerce minor mistakes in encoding and make it easier to embed your JSON in HTML and XML.

Output

The output is well-formed JSON as defined by RFC 4627. The output satisfies three additional properties:

  1. The output will not contain the substring (case-insensitively) "</script" so can be embedded inside an HTML script element without further encoding.
  2. The output will not contain the substring "]]>" so can be embedded inside an XML CDATA section without further encoding.
  3. The output is a valid Javascript expression, so can be parsed by Javascript's eval builtin (after being wrapped in parentheses) or by JSON.parse. Specifically, the output will not contain any string literals with embedded JS newlines (U+2028 Paragraph separator or U+2029 Line separator).

Security

Since the output is well-formed JSON, passing it to eval will have no side-effects and no free variables, so is neither a code-injection vector, nor a vector for exfiltration of secrets.

This library only ensures that the JSON string → Javascript object phase has no side effects and resolves no free variables, and cannot control how other client side code later interprets the resulting Javascript object. So if client-side code takes a part of the parsed data that is controlled by an attacker and passes it back through a powerful interpreter like eval or innerHTML then that client-side code might suffer unintended side-effects.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .