PHP:HTML属性编码/JavaScript解码

7

如何正确地为HTML属性上下文编码不可信数据?例如:

<input type="hidden" value="<?php echo $data; ?>" />

我通常使用 htmlentities() 或者 htmlspecialchars() 来实现这一功能:
<input type="hidden" value="<?php echo htmlentities($data); ?>" />

然而,最近我遇到了一个问题,当我需要传递的数据是一个需要交给JavaScript更改页面位置的URL时,这个方法会破坏我的应用程序。
<input id="foo" type="hidden" value="foo?bar=1&amp;baz=2" />
<script>
    // ...
    window.location = document.getElementById('foo').value;
    // ...
</script>

在这种情况下,foo是一个C程序,它不会理解URL中的编码字符并导致段错误。
我可以在JavaScript中简单地获取值并执行类似于value.replace('&amp;', '&')的操作,但那看起来有些笨拙,而且只适用于“&”符号。
因此,我的问题是:是否有更好的方法来处理注入HTML属性的数据的编码或解码?
我已经阅读了所有的OWASP XSS Prevention Cheatsheet,它让我感觉只要小心引用我的属性,那么我需要编码的唯一字符就是引号本身(")-在这种情况下,我可以使用类似于str_replace('"', '&quot;', ...)的东西,但我不确定自己是否正确理解了它。

2
urlencode在PHP中不是已经处理了吗?在php手册的注释中也有一些代码示例,展示如何防止XSS攻击。http://php.net/manual/en/function.urlencode.php - GillesC
@gillesc:urlencode()用于编码URL的参数,而不是整个URL,并且不会为HTML属性上下文编码。手册中甚至有一节讲到了这一点 - “将其保留为&,但只需使用htmlentities()或htmlspecialchars()对您的URL进行编码即可”。 - FtDRbwLXw6
你确定 window.location = document.getElementById('foo'); 是正确的吗?我认为应该改为 window.location = document.getElementById('foo').value;,并且它会重定向到正确的页面(foo?bar=1&baz=2)。 - Okan Kocyigit
@ocanal:谢谢,我已经纠正了这个问题,但这并没有解决问题,因为它会重定向到 foo?bar=1&amp;baz=2。PHP 能够理解这一点,但 foo 不是 PHP 脚本,除非 URL 像 foo?bar=1&baz=2,否则它会崩溃。 - FtDRbwLXw6
5个回答

11

你目前使用 htmlentities()htmlspecialchars() 的方法是正确的。

你提供的示例是正确的 HTML:

<input id="foo" type="hidden" value="foo?bar=1&amp;baz=2" />

在 value 属性中的 & 符号确实需要 HTML 编码,否则您的 HTML 是无效的。大多数浏览器会正确解析含有 & 的代码,但这并不改变它无效的事实,因此您编码它是正确的。

你的问题不在于值的编码,它是正确的,而在于你使用的 JavaScript 代码没有正确地解码它。

实际上,我对此感到惊讶,因为你的 JS 代码正在访问 DOM,而 DOM 应该返回已解码的值。

我写了一个 JSFiddle 来证明这一点:http://jsfiddle.net/qRd4Z/

运行它,它会像我预期的那样给我一个带有已解码值的警告框。将其更改为 console.log 也会给出我期望的结果。因此,我不确定为什么你得到不同的结果?也许你使用的是不同的浏览器?建议指定你正在测试的浏览器。或者你可能错误地双重编码了实体?请确认这不是这种情况。


5
如果您在属性值周围添加双引号,则htmlspecialchars()就足够了。
 <input id="foo" type="hidden" value="foo?bar=1&amp;baz=2" />

这是正确的,浏览器将发送解码后的foo?bar=1&baz=2(编码为&amp;)到服务器。如果服务器没有看到foo?bar=1&baz=2,那么您必须对值进行两次编码。

在JavaScript中获取该值也应该返回foo?bar=1&baz=2(例如,document.getElementById('foo').value 必须返回foo?bar=1&baz=2)。

使用浏览器查看页面源代码,查看输入字段的实际源代码。

如果您正在使用JavaScript修改输入字段的值,则脚本必须对其进行双重编码。

顺便说一下,您的程序不应因用户输入错误而崩溃;)


0
请注意,直接使用htmlentities是无法解决问题的!
默认情况下,它只编码" < > &
它不会转义',这可能会导致问题!
确保在函数中使用标志,您可以在此处找到用法和示例。

谢谢,但这只有在您没有使用"字符正确分隔属性值时才会产生影响,而我已经这样做了。省略定界符或使用'作为定界符是不好的编程习惯。 - FtDRbwLXw6

0

0

您可以使用DOM解码该值:

function decodeHTMLSpecialChars(input){
  var div = document.createElement('div');
  div.innerHTML = input;
  return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue;
}

这将呈现以下字符串:
'http://someurl.com/foo?bar=1&amp;baz=2'

转换为:

decodeHTMLSpecialChars('http://someurl.com/foo?bar=1&amp;baz=2');
// => 'http://someurl.com/foo?bar=1&baz=2

不过,对于HTML的编码和解码来说,使用htmlspecialchars和HTML转义是标准方法,能够很好地完成工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接