3

I want to access data in a PHP variable from JavaScript code. An (old) OWASP cheat sheet recommends embedding it in HTML before accessing it, an approach like this:

<div id="init_data" style="display: none">
 <%= html_encode(data.to_json) %>
</div>

A few SE answers refer to that cheat sheet, but suggest instead using the script element with type="application/json":

<script id="init_data" type="application/json">
    <?php echo htmlspecialchars(json_encode($untrustedData)); ?>
</script>

But is htmlspecialchars even necessary in this case? According to MDN, this is a "data block which won't be processed by the browser". It should never be executed as JavaScript, if not handling it improperly later.

I have tried naive inputs such as <script>, <!-- and </script>. The first two are correctly interpreted by my browser as data and the last is escaped into <\/script> and then interpreted as data. But I haven't found anyone certifying this is a good idea, and I wouldn't be certain I haven't missed anything. Is this construct vulnerable to XSS attacks or anything else?

<script id="init_data" type="application/json">
    <?php echo json_encode($untrustedData); ?>
</script>

1 Answer 1

2

Even though a data block won't be processed by the browser, the question is whether or not the JSON string is able to escape the data block in the context of HTML. If we're talking about language-agnostic code, the following IS actually valid JSON:

{ 
  "foo": "</script><script>alert(1)</script>" 
}

So if we output this JSON string directly to the browser, the following HTML document is vulnerable to XSS (JSFiddle):

<script id="init_data" type="application/json">
    { "foo": "</script><script>alert(1)</script>" }
</script>

When talking specifically about PHP, the json_encode() function escapes all forward slashes as \/, which makes </script> tags become <\/script>, which would mitigate XSS vulnerabilities when outputted into a data block. In this case, htmlspecialchars() should be unnecessary, unless there is some sort of a content-encoding vulnerability or some other way to get the output JSON string to be interpreted by the browser as </script> to end the current script tag.

However, JSON encode functions from other languages may or may not behave similarly. For example, Javascript converts < to \x3C, but leaves the / as-is:

JSON.stringify({ foo: '</script>' }) // Output: '{"foo":"\x3C/script>"}'

But there may be other languages that would not escape either the < nor the /, which would still be valid JSON, but vulnerable to XSS attacks if outputted directly within an HTML document.

So when using PHP to output the JSON string to the page, make sure language/library used to generate the JSON string does actually prevent XSS attacks. Of course, it goes without saying that nothing from user input should ever be outputted directly to the page.

2
  • ... and don't base64_encode to save to the database and only echo base64_decode() to send to the page...
    – ThoriumBR
    Aug 8, 2022 at 17:21
  • 1
    @ThoriumBR Yes, of course, but that's not really within the scope of the question.
    – Mike
    Aug 8, 2022 at 17:59

You must log in to answer this question.

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