如何在JavaScript中取消转义HTML?

18

我正在使用一个Web服务,它会给我类似以下的值:

var text = "<<<&&&";
我需要使用JavaScript将其打印成“<<<&&&”。但是有个问题:我不能使用innerHTML(实际上,我要将这些值发送到一个创建文本节点的原型库中,以便它不会解码我的原始HTML字符串)。如果无法编辑库文件,您将如何取消转义此HTML?我需要了解这里的真正问题,取消转义此类型的字符串的风险是什么?innerHTML 是如何做到的?还有哪些其他选项可用? 注:这里的问题不在于使用JavaScript正常的escape/unescape,甚至不在于使用jQuery/Prototype实现它们,而在于使用任何一种方式可能带来的安全问题...也就是“他们告诉我使用它们非常不安全”的问题。如果想要理解innerHTML是如何解码这个奇怪的字符串,请查看这个简单的示例:
<html>
<head>
<title>createTextNode example</title>

<script type="text/javascript">

var text = "&lt;&lt;&lt;&amp;&amp;&amp;";
function addTextNode(){
    var newtext = document.createTextNode(text);
    var para = document.getElementById("p1");
    para.appendChild(newtext);
}
function innerHTMLTest(){
    var para = document.getElementById("p1");
    para.innerHTML = text;
}
</script>
</head>

<body>
<div style="border: 1px solid red">
<p id="p1">First line of paragraph.<br /></p>
</div><br />

<button onclick="addTextNode();">add another textNode.</button>
<button onclick="innerHTMLTest();">test innerHTML.</button>

</body>
</html>

这个问题怎么可能是重复的?这个问题比被认为是重复的那个问题还要早。 - ands
您可以在类似问题的答案中看到使用innerHTML存在的安全问题。为了避免XSS漏洞,您应该使用he库。您可以在另一个类似问题的答案中看到代码示例。 - ands
6个回答

11

把你的测试字符串改为&lt;b&gt;&lt;&lt;&amp;&amp;&amp;&lt;/b&gt;,以更好地了解风险... 或者更好的是,使用&lt;img src='http://www.spam.com/ASSETS/0EE75B480E5B450F807117E06219CDA6/spamReg.png' onload='alert(document.cookie);'&gt;来进行窃取cookie的垃圾邮件。

请参见http://jsbin.com/uveme/139/上的示例(基于您的示例,使用原型进行转义)。尝试点击四个不同的按钮以查看不同的效果。只有最后一个存在安全风险。(您可以在http://jsbin.com/uveme/139/edit中查看/编辑源代码)该示例实际上并不会窃取您的cookie...

  1. 如果你的文本来自已知安全的来源,并且不基于任何用户输入,那么你是安全的。
  2. 如果您使用createTextNode创建文本节点并使用appendChild将未更改的节点对象直接插入到您的文档中,那么您是安全的。
  3. 否则,您需要采取适当的措施,以确保不安全的内容无法传递到您的查看器的浏览器。
注意:正如Ben Vinegar所指出的,使用createTextNode并不是万能的解决方案:使用它来转义字符串,然后使用textContent或者innerHTML获取转义后的文本并对其进行其他操作,在随后的使用中并不能保护你。特别地,如果用于填充属性,则Peter Brown的下面的escapeHtml方法并不安全。

这真的很有用。因此,归根结底,如果来自用户的任何内容,都应该是TextNode吗? - DFectuoso
@DFectuoso:这是一种方法,如果您不希望他们能够使用任何HTML功能,则可以使用该方法。例如,如果您希望他们为其文本设置样式,则必须安全地解决此问题... - Stobor
有趣的洞察安全问题。 - Milad Naseri
如果您正在使用createTextNode方法,那么您是安全的:不! 根据http://benv.ca/2012/10/2/you-are-probably-misusing-DOM-text-methods/ 的说法。 - user
@buffer: Ben在没有上下文的情况下引用了我的答案,这有点不太光彩。然而,他说对了另一件事:使用createTextNode构建escapeHtml函数可能是不安全的。虽然此页面上的任何答案都没有建议这样做,但我的措辞可能会让其他人觉得在网络上使用createTextNode的函数比适当的更安全。我已经添加了关于这个问题的澄清。 - Stobor

5

一个非常好的阅读材料是http://benv.ca/2012/10/4/you-are-probably-misusing-DOM-text-methods/,其中解释了为什么惯用的使用createTextNode方法实际上并不安全。

这篇文章中举出的一个代表性例子说明了风险:

function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
};

var userWebsite = '" onmouseover="alert(\'derp\')" "';
var profileLink = '<a href="' + escapeHtml(userWebsite) + '">Bob</a>';
var div = document.getElementById('target');
div.innerHtml = profileLink;
// <a href="" onmouseover="alert('derp')" "">Bob</a>

1
在构建用于填充元素属性的“escapeHtml”方法时,它并不安全。然而,他的观点是正确的:如果您不能100%确定函数使用的上下文环境,则无法确定此函数是否安全。在像document.getElementById(“whereItGoes”).appendChild(document.createTextNode(unsafe_str));这样的结构中适当地使用createTextNode,这不是他所评论的内容... - Stobor

2

只是一些猜测。

innerHTML 字面上是浏览器解释 HTML。

所以 < 变成小于号,因为如果在 HTML 文档中放置 <,那么就会发生这种情况。

带有 & 的字符串的最大安全风险是 eval 语句,任何 JSON 都可能使应用程序不安全。我不是安全专家,但如果字符串保持为字符串,则应该没问题。

这是 innerHTML 安全的另一种方式,未转义的字符串正在成为 HTML,因此没有运行 JavaScript 的风险。


1
function mailpage()
{ mail_str =  "mailto:?subject= Check out the " + escape( document.title ); 
      mail_str += "&body=" + escape("I thought you might be interested in the " + document.title + ".\n\n" );
      mail_str += escape("You can view it at " + location.href + ".\n\n");
      location.href = mail_str;
}

我刚刚发布的答案允许您将实际页面标题(使用&amp;或#38;)放在主题行中。...并且HTML页面的正文将显示在电子邮件的正文中。 - Jan

1
只要您的代码创建文本节点,浏览器就不应该渲染任何有害内容。实际上,如果您使用Firebug或IE Dev Toolbar检查生成的文本节点源代码,您会发现浏览器正在重新转义特殊字符。
给它一个机会。
"<script>"

然后它重新转义为:

"&lt;script&gt;"

有几种类型的节点:元素、文档、文本、属性等。

危险在于浏览器将字符串解释为包含脚本。innerHTML 属性容易受到这个问题的影响,因为它会指示浏览器创建元素节点,其中一个可能是脚本元素,或者具有内联 JavaScript,例如 onmouseover 处理程序。创建文本节点可以避免这个问题。


虽然我无法通过 &lt;script&gt;alert('hi');&lt;/script&gt; 做出任何坏事 - 出于某种原因,尽管脚本被插入,但它并没有被执行。但是图片的 onload 事件被触发了,所以我利用了这一点... - Stobor
@Stobor - 你能给我展示一下你的意思吗?我很好奇... - Jeff Meatball Yang
@Jeff:好久不见,但我刚看到你的问题。我的意思是我无法运行此页面上的脚本:http://jsbin.com/onezo - 尽管查看计算源代码显示脚本标记,但它不会alert()... 但是,我回答中的警报有效。 - Stobor

1

据我所知,使用该方法对HTML进行反转义可能会导致一些严重的安全问题...这正是我的观点。 - DFectuoso
4
没问题,你回答后我就完成了这件事……不要对这个人点踩! - DFectuoso
3
转义和反转义函数现在已被弃用。有关详细信息,请参见例如此博客文章 - Ville
有时候你可能想要对自己的代码进行反转义,以避免安全问题。但是需要注意的是,unescape 并不总是有效的,例如它无法对 &lt; 进行反转义。 - raquelhortab
以上博客文章链接已移至此处:https://cwestblog.com/2011/05/23/escape-unescape-deprecated/ - tresf
请注意,如果有人使用我之前留言中的 polyfill,请确保应用评论中的修复程序,因为它们是必需的。 - tresf

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