I understand that the use of eval(json_str)
on the client is vulnerable to malicious code. My question is, if json_str
was an array constructed by the PHP function json_encode
, would I be safe?
For example,
json_str = json_encode(array(record1,
record2,
record3));
would it now be entirely safe to use eval(json_str)
inside client-side code?
Yes and no:
Yes: PHP produces valid JSON
No: PHP may as well return malicious code as in JSON.
If you can trust the source, or if you even have full control over it (because its yours), there is no problem.
In terms of pure JavaScript, yes, you are safe: the output of json_encode
can never containing anything but static values which will have no unexpected side effected when passed to eval
. (Though you typically have to surround your JSON string with ()
when using eval
, to avoid it misinterpreting an object literal expression as a statement block.)
Aside: this is not necessarily true of all JSON encoders because there are some characters that are valid to include raw in a JSON string that are not valid raw in JavaScript. Most notably, U+2028 and U+2029 which can't go unescaped in JavaScript string literals as they constitute newlines. However PHP's encoder encodes all non-ASCII characters (eg as "\u2028"
) so no issue here.
In terms of JavaScript embedded in another language (typically: HTML) you are not necessarily safe. For example:
<script type="text/javascript">
var v= <?php echo json_encode($value); ?>;
</script>
In this example, what if value
contains a string with the character sequence </script
? This would allow the value to end the script block prematurely and thus escape into HTML markup, where it could then inject other malicious script.
To avoid this problem, when including JSON content in HTML, always encode the <
character in string literals, as \x3C
or, in JSON-compliant terms, \u003C
. For compatibility with XHTML non-CDATA script blocks, do &
as well. For compatibility with JS inside event handler attributes, do quotes as well.
PHP will do this for you with the right options to json_encode()
:
var v= <?php echo json_encode($value, JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS); ?>;
(You may want to define a shortcut function to make this quicker to write.)
If you want to use Content Security Policy (CSP), you are prevented from executing inline script tags. This would therefore would make bobince's otherwise amazing answer impossible since CSP requires that all JavaScript be in separate files.
How to get your JavaScript in a separate file:
One way around this is to html encode the JSON with PHP (which should prevent XSS) and then echo it to a hidden element and then use JavaScript to get the contents of that tag (adapted from OWASP):
Put this inline (note, it won't actually be executed):
<script id="jsonString" type="application/json">
<?php
// OWASP uses a <script> tag with an invalid "type" attribute, but
// you can just as easily use a <span style="display:none"> or other
// hidden tag.
//
// Note, this probably won't actually be valid JSON because we are using
// htmlspecialcharacters() on the JSON data. That doesn't matter because
// this is never actually executed by the browser due to the incorrect
// script type. We will decode it later into valid JSON.
echo htmlspecialchars(json_encode($object), ENT_NOQUOTES);
?>
</script>
Then in your JavaScript file:
var dataElement = document.getElementById('jsonString');
// Get innerText, which also has the side effect of html decoding
// the string returned, which is just what we want
var jsonString = dataElement.textContent || dataElement.innerText;
// Now you can parse the JSON string
var jsonObj = JSON.parse(jsonString);
Don't use eval for JSON parsing
Don't do it.
It's very likely that your server will never become compromised, and that your application will be mostly secure, blah blah blah, that's not the point. It is possible for a server to become partially compromised (too many vectors of attack, what if the php json_encode
function became compromised on your server?).
The simple solution is not to trust anything sent by anyone. Modern browsers have native JSON parsers, and www.json.org provides a long list of JSON parsers for various different languages. The JS versions will fall back on the native implementation for speed.
What all this means is that there's no good reason to use eval
for JSON parsing.
It should be safe, but on the client, you aren't guaranteed that the json_str
hasn't been injected by some other source.