协慌网

登录 贡献 社区

从输入字段读取属性时,HTML 编码丢失

我正在使用 JavaScript 从隐藏字段中提取值并将其显示在文本框中。隐藏字段中的值被编码。

例如,

<input id='hiddenId' type='hidden' value='chalk &amp; cheese' />

被拉入

<input type='text' value='chalk &amp; cheese' />

通过一些 jQuery 从隐藏字段中获取值(在这一点上,我失去了编码):

$('#hiddenId').attr('value')

问题是,当我阅读chalk &amp; cheese从隐藏字段起, chalk &amp; cheese似乎失去了编码。我不希望这个价值是chalk & cheese 。我想要文字amp;被保留。

是否存在将对字符串进行 HTML 编码的 JavaScript 库或 jQuery 方法?

答案

编辑:这个答案很久以前发布了,并且htmlDecode函数引入了 XSS 漏洞。已对其进行了修改,将临时元素从div更改为textarea从而减少了 XSS 机会。但是现在,我鼓励您使用其他 anwswer 中建议的 DOMParser API。


我使用以下功能:

function htmlEncode(value){
  // Create a in-memory element, set its inner text (which is automatically encoded)
  // Then grab the encoded contents back out. The element never exists on the DOM.
  return $('<textarea/>').text(value).html();
}

function htmlDecode(value){
  return $('<textarea/>').html(value).text();
}

基本上,div 元素是在内存中创建的,但是永远不会附加到文档中。

htmlEncode函数上,我设置了元素的innerText ,并检索了编码的innerHTML ;在htmlDecode函数上,我设置了元素的innerHTML值,并检索了innerText

在此处查看运行示例。

jQuery 技巧不对引号进行编码,而在 IE 中,它将删除空格。

基于 Django 中的转义 templatetag(我猜它已经被大量使用 / 测试),我制作了此函数来完成所需的工作。

可以说,它比空白消除问题的任何解决方法都更简单(并且可能更快),并且它对引号进行了编码,例如,如果您要在属性值中使用结果,则必须使用引号。

function htmlEscape(str) {
    return str
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

// I needed the opposite function today, so adding here too:
function htmlUnescape(str){
    return str
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, "'")
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&');
}

更新 2013-06-17:
在寻找最快的转义过程中,我发现了replaceAll方法的以下实现:
http://dumpsite.com/forum/index.php?topic=4.msg29#msg29
(此处也引用: 替换字符串中所有字符实例的最快方法
一些性能结果在这里:
http://jsperf.com/htmlencoderegex/25

它为上面的内置replace链提供了相同的结果字符串。如果有人可以解释为什么它更快,我将非常高兴!

更新 2015-03-04:
我只是注意到 AngularJS 正使用上面的方法:
https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435

它们增加了一些改进 - 它们似乎正在处理一个晦涩的 Unicode 问题 ,以及将所有非字母数字字符转换为实体。我的印象是,只要您为文档指定了 UTF8 字符集,就不需要后者。

我会注意到(4 年后),Django 仍然不做任何一件事情,所以我不确定它们的重要性:
https://github.com/django/django/blob/1.8b1/django/utils/html.py#L44

更新 2016-04-06:
您可能还希望转义正斜杠/ 。正确的 HTML 编码不需要这样做,但是OWASP 建议将其作为防 XSS 安全措施。 (感谢 @JNF 在评论中建议这一点)

.replace(/\//g, '&#x2F;');

这是一个非 jQuery 版本,比 jQuery .html()版本和.replace()版本都快得多。这样会保留所有空格,但是像 jQuery 版本一样,它不处理引号。

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

速度: http //jsperf.com/htmlencoderegex/17

速度测试

演示: jsFiddle

输出:

输出

脚本:

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

function htmlDecode( html ) {
    var a = document.createElement( 'a' ); a.innerHTML = html;
    return a.textContent;
};

document.getElementById( 'text' ).value = htmlEncode( document.getElementById( 'hidden' ).value );

//sanity check
var html = '<div>   &amp; hello</div>';
document.getElementById( 'same' ).textContent = 
      'html === htmlDecode( htmlEncode( html ) ): ' 
    + ( html === htmlDecode( htmlEncode( html ) ) );

HTML:

<input id="hidden" type="hidden" value="chalk    &amp; cheese" />
<input id="text" value="" />
<div id="same"></div>