JSON_HEX_TAG
如果您在 HTML 文档中回显到 JS(就像您在示例中一样),这是必要的,否则您可能会打开一个巨大的 XSS 漏洞。如果没有这个,攻击者可以发布类似以下内容的内容:
</script>alert("XSS");</script>
自 PHP 5.4 以来,此问题已得到修复,并且默认情况下会转义斜杠,但您仍然永远不知道您的代码将在哪个 PHP 版本上运行。安全总比后悔好。
正如 OP 指出的那样,即使在 PHP 的任何版本中,您都可以插入 a<!--来弄乱整个页面,这可能会导致意外行为。所以在所有 PHP 版本中确实需要这个标志。
JSON_HEX_QUOT 和 JSON_HEX_APOS
要了解它的作用,请查看以下示例:
$array = array(
"a" => "'",
"b" => '"',
);
// This will output {"a":"'","b":"\""}
echo json_encode($array);
// This will output {"a":"\u0027","b":"\u0022"}
echo json_encode($array, JSON_HEX_QUOT | JSON_HEX_APOS);
所以字符串文字周围的引号永远不会被编码。字符串文字中的引号将在没有标志的情况下进行转义,并使用它们进行编码。
根据您的参考资料,这用于使输出在事件处理程序中安全使用(即 HTML 属性)。确实,它们在脚本标签中并不是严格需要的,但它们在事件处理程序中是安全的并不完全正确。
例如看一下这个例子:
<?php $data = array(" onmouseenter=alert(1) " => "foo"); ?>
<a onclick="x = <?= json_encode($data, JSON_HEX_QUOT | JSON_HEX_APOS); ?>">test</a>
导致:
<a onclick="x = {" onmouseenter=alert(1) ": "foo"}">test</a>
我认为如果你总是用单引号将你的属性值括起来,你是安全的,但这仍然感觉有点冒险。
JSON_HEX_AMP
在这里引用您的参考:
为了与 XHTML 非 CDATA 脚本块兼容,也可以使用 &。
因此,由于您正在使用 HTML5,因此这不适用于您,而且我认为这里不存在安全漏洞。尽管如此,编码并没有什么坏处。
结论
- 对于您的使用 - 在 HTML5 中的脚本标记内 - 只需使用
JSON_HEX_TAG就足够了。
- 在属性(事件处理程序)中执行此操作是危险的,至少除非您用单引号引起来。
- 如果我是你,我会创建一个名为的小辅助函数
safe_json_encode,它使用所有四个标志,然后只在脚本标签中使用它。过度编码不会伤害您。
补充说明
- 确保为页面提供正确的内容类型和字符编码。搞砸这个可能会导致绕过编码的方法。
- 如果稍后将变量值输出到 HTML,则需要再次考虑 XSS。例如,使用 JSON 中的值
innerHTML或document.write将不安全。(我看到你在脚本的第二行处理了这个问题。)
免责声明:这是基于我今天所做的研究。我不是 PHP 大师。如果您依靠它来做一些关键的事情,您可能想自己做更多的研究,以确保我在这里没有遗漏任何东西。