nette / utils

🛠 Lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.
https://doc.nette.org/utils
Other
1.98k stars 147 forks source link

JSON_UNESCAPED_SLASHES is a dangerous default #235

Closed ondrejmirtes closed 3 years ago

ondrejmirtes commented 4 years ago

When I'm outputting JSON with user input in an HTML <script> tag:

<script><?php echo \Nette\Utils\Json::encode($data); ?></script>

it breaks the page if the $data include </script>. When I use the built-in function:

<script><?php echo json_encode($data); ?></script>

it works fine as it escapes forward slashes.

I have two questions:

1) Why is JSON_UNESCAPED_SLASHES the default? I can't even get rid of it using $flags. 2) Does Latte deal with this with context-sensitive escaping? How?

Thank you.

janpecha commented 4 years ago

Latte uses method Filters::escapeJs.

IMHO your code is just wrong, right solution is this (or something similar):

<script><?php echo \Latte\Runtime\Filters::escapeJs($data); ?></script>
ondrejmirtes commented 4 years ago

My point is that Json::encode because of this is not a drop-in replacement for json_encode so people who just switch to it are vulnerable to bugs like this.

I'm in an environment where I don't use Latte (I'm taking statically generated HTML and injecting a single JSON object in there before sending as an HTTP response) so that's why it might seem weird.

janpecha commented 4 years ago

My point is you can't depend on internal behavior of Json::encode - it converts input to valid JSON not to HTML-SCRIPT-JSON so you must use right function for right output. And yes, Json::encode isn't 1:1 replacement for json_encode but it's not goal IMHO.

For 1:1 replacement of json_encode you can use Nette\Safe::json_encode.

dg commented 4 years ago

<script><?php echo \Nette\Utils\Json::encode($data); ?></script> is definitely not safe. It was never my goal, because I have a Latte for HTML. But I think json_encode() is not safe either because it doesn't escape sequences like <! or ]]>.

Should Json::encode() escape HTML? I am not sure… What about <div data-info="<?php echo \Nette\Utils\Json::encode($data); ?>"></div>

ondrejmirtes commented 4 years ago

Thank you for the suggestions. I'm not an escaping expert so feel free to close this issue :)

ondrejmirtes commented 4 years ago

Oh, there's a bug in Latte\Runtime\Filters::escapeJs.

This didn't work for me:

str_replace([']]>', '<!'], [']]\x3E', '\x3C!'], $json);

There need to be double quotes:

str_replace([']]>', '<!'], ["]]\x3E", "\x3C!"], $json)