9

I'm seeing plenty of examples when the output is going into HTML directly, but I'm seeing more conflicting information when the output is going straight into JS.

For example if the code was:

var thisIsATest = '<?php echo $_GET['a']; ?>';

And the URL attack vector was:

?a=';alert(1);'

The output renders as:

var thisIsATest = '';alert(1);'';

I've seen plenty of docs recommending what to avoid (such as addslashes), and references to htmlspecialchars() (but not by itself)...

What is the most correct approach?

Anders
  • 64,406
  • 24
  • 178
  • 215
Jason
  • 93
  • 4

1 Answers1

13

The correct way is to use the tools of your framework (and its template engine, if available). If you're fiddling with PHP in JS strings, you probably make life harder and more dangerous than necessary.

With plain PHP a common and safe approach is to use json_encode() as explained here. E.g.:

var foo = <?php echo json_encode($foo, JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS); ?>

json_encode() returns the JSON representation of a value, hence it's guaranteed to evaulate to a valid object in your JS code and you can just assign it to a variable as shown. But don't omit the additional flags. Depending on the context, an attacker could otherwise use payloads like </script> to break out of the entire script tag.

The functions htmlentities() and htmlspecialchars() you referred to are used for direct HTML output and should not be employed on JS. They would also allow your string to contain line breaks, resulting in a syntax error that might have security consequences.

Talking about frameworks, Wordpress gives you the wrapper wp_json_encode(), as recommended in this guideline. There might be equivalent functions for your own framework.

Also be aware that with json_encode() the object is safely prepared for a JS context, but any string you pass can still contain HTML tags that would be unsafe to insert via, say, document.write() or innerHTML. (Avoid these two altogether.)

This is silly, but XSS-safe:

Hello, <span id="display"></span>!
<?php $name = $_GET['name']; ?>
<script>
    var name = <?php echo json_encode($name, JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS); ?>;
    display.textContent = name;
</script>
Arminius
  • 43,922
  • 13
  • 140
  • 136