关于javascript:当从输入字段读取属性时,HTML编码丢失

HTML-encoding lost when attribute read from input field

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

例如,

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

被拉进去

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

通过一些jQuery从隐藏字段中获取值(此时我丢失了编码):

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

问题是,当我从隐藏字段中读取chalk & cheese时,JavaScript似乎失去了编码。 我不希望值为chalk & cheese。 我希望保留文字amp;

是否有一个JavaScript库或jQuery方法将对字符串进行HTML编码?


编辑:这个答案很久以前发布了,htmlDecode函数引入了一个XSS漏洞。它已被修改,将临时元素从div更改为textarea,从而减少了XSS的几率。但是现在,我鼓励您按照其他anwswer的建议使用DOMParser API。

我使用这些功能:

1
2
3
4
5
6
7
8
9
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中的转义模板标签,我猜我已经大量使用/测试了,我做了这个功能,它可以满足需要。

它可以说比空白剥离问题的任何变通方法更简单(也可能更快) - 并且它编码引号,如果您要在属性值中使用结果,这是必不可少的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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在评论中提出建议)

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


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

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

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

speed test

演示: jsFiddle

输出:

output

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 = '   &amp; hello';
document.getElementById( 'same' ).textContent =
      'html === htmlDecode( htmlEncode( html ) ): '
    + ( html === htmlDecode( htmlEncode( html ) ) );

HTML:

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


我知道这是一个旧的,但我想发布一个接受的答案的变体,将在IE中工作而不删除行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function multiLineHtmlEncode(value) {
    var lines = value.split(/

|
|
/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('

'
);
}

function htmlEncode(value) {
    return $('').text(value).html();
}


Underscore提供了执行此操作的_.escape()_.unescape()方法。

1
2
3
4
5
> _.unescape("chalk &amp; cheese" );
 "chalk & cheese"

> _.escape("chalk & cheese" );
 "chalk &amp; cheese"


好答案。请注意,如果要使用jQuery 1.4.2编码的值为undefinednull,则可能会出现以下错误:

jQuery("").text(value).html is not a function

要么

Uncaught TypeError: Object has no method 'html'

解决方案是修改函数以检查实际值:

1
2
3
4
5
6
7
function htmlEncode(value){
    if (value) {
        return jQuery('').text(value).html();
    } else {
        return '';
    }
}


对于那些喜欢普通javascript的人来说,这是我成功使用的方法:

1
2
3
4
5
6
7
function escapeHTML (str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}

FWIW,编码不会丢失。在页面加载期间,标记解析器(浏览器)使用编码。一旦读取并解析了源并且浏览器将DOM加载到内存中,编码就会被解析为它所代表的内容。因此,当您的JS执行以读取内存中的任何内容时,它获取的char就是编码所代表的内容。

我可能在这里严格操作语义,但我希望你理解编码的目的。"迷失"这个词听起来似乎有些东西不像它应该的那样工作。


没有Jquery更快。您可以对字符串中的每个字符进行编码:

1
function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}

或者只是针对主角担心(&amp;,inebreaks,<,>,"和'),如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!

"Safe" escape <script id=\'\'> & useful in [cc lang="javascript"] tags!'
);

testing.innerHTML=test.value;

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
1
2
3
<textarea id=test rows="9" cols="55"></textarea>

www.WHAK.com


Prototype内置了String类。因此,如果您正在使用/计划使用Prototype,它会执行以下操作:

1
2
'This is an article'.escapeHTML();
// ->"&lt;div class="article"&gt;This is an article&lt;/div&gt;"


这是一个简单的JavaScript解决方案。它使用方法"HTMLEncode"扩展String对象,该方法可用于没有参数的对象或参数。

1
2
3
4
5
6
7
8
9
10
11
12
String.prototype.HTMLEncode = function(str) {
  var result ="";
  var str = (arguments.length===1) ? str : this;
  for(var i=0; i<str.length; i++) {
     var chrcode = str.charCodeAt(i);
     result+=(chrcode>128) ?"&#"+chrcode+";" : str.substr(i,1)
   }
   return result;
}
// TEST
console.log("stetaewteaw ??".HTMLEncode());
console.log("stetaewteaw ??".HTMLEncode("??????"))

我已经为"javascript的HTMLEncode方法"做了一个主旨。


基于angular的sanitize ...(es6模块语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// ref: https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js
const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
const NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;

const decodeElem = document.createElement('pre');


/**
 * Decodes html encoded text, so that the actual string may
 * be used.
 * @param value
 * @returns {string} decoded text
 */

export function decode(value) {
  if (!value) return '';
  decodeElem.innerHTML = value.replace(/</g, '&lt;');
  return decodeElem.textContent;
}


/**
 * Encodes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} encoded text
 */

export function encode(value) {
  if (value === null || value === undefined) return '';
  return String(value).
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, value => {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, value => {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

export default {encode,decode};


我有一个类似的问题,并使用JavaScript中的函数encodeURIComponent解决它(文档)

例如,在您的情况下,如果您使用:

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

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

你会得到chalk%20%26%20cheese。甚至保留空间。

在我的情况下,我不得不编码一个反斜杠,这段代码完美无缺

1
encodeURIComponent('name/surname')

我得到了name%2Fsurname


您不必转义/编码值,以便将它们从一个输入字段移动到另一个输入字段。

1
2
3
4
5
6
7
8
9
10
11
<form>
 <input id="button" type="button" value="Click me">
 <input type="hidden" id="hiddenId" name="hiddenId" value="I like cheese">
 <input type="text" id="output" name="output">
</form>

    $(document).ready(function(e) {
        $('#button').click(function(e) {
            $('#output').val($('#hiddenId').val());
        });
    });

JS不会插入原始HTML或任何东西;它只是告诉DOM设置value属性(或属性;不确定)。无论哪种方式,DOM都会为您处理任何编码问题。除非你做的事情比使用document.writeeval更奇怪,否则HTML编码将是有效透明的。

如果你正在谈论生成一个新的文本框来保存结果......它仍然很容易。只需将HTML的静态部分传递给jQuery,然后在它返回给您的对象上设置其余的属性/属性。

1
$box = $('<input type="text" name="whatever">').val($('#hiddenId').val());


afaik在javascript中没有任何直接的HTML Encode / Decode方法。

但是,你可以做的是使用JS创建一个任意元素,设置它的内部文本,然后使用innerHTML读取它。

说,使用jQuery这应该工作:

1
2
3
var helper = $('chalk & cheese').hide().appendTo('body');
var htmled = helper.html();
helper.remove();

或者沿着这些方向的东西


我的纯JS功能:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * HTML entities encode
 *
 * @param {string} str Input text
 * @return {string} Filtered text
 */

function htmlencode (str){

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

JavaScript HTML Entities Encode & Decode


如果你想使用jQuery。我找到了这个:

http://www.jquerysdk.com/api/jQuery.htmlspecialchars

(jQuery SDK提供的jquery.string插件的一部分)

我相信Prototype的问题在于它扩展了JavaScript中的基础对象,并且与您可能使用的任何jQuery不兼容。当然,如果你已经在使用Prototype而不是jQuery,那就不会有问题了。

编辑:还有这个,它是Prototype的jQuery字符串实用程序的一个端口:

http://stilldesigning.com/dotstring/


1
2
3
4
5
6
7
8
9
10
11
12
String.prototype.htmlEncode = function () {
    return String(this)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');

}

var aString = 'alert("I hack your site")';
console.log(aString.htmlEncode());

将输出:<script>alert("I hack your site")</script>

一旦定义,所有字符串都可以访问.htmlEncode()。


HtmlEncodes给定的值

1
2
3
4
5
6
7
8
  var htmlEncodeContainer = $('');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }


我在Domain User字符串中遇到了反斜杠的一些问题。

我把这个添加到Anentropic答案的其他逃脱中

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

我在这里找到的:
如何在JavaScript中逃避反斜杠?


这里有一点模仿Microsoft的ASP中的Server.HTMLEncode函数,用纯JavaScript编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function htmlEncode(s) {
  var ntable = {
   "&":"amp",
   "<":"lt",
   ">":"gt",
   """:"quot"
  };
  s = s.replace(/[&<>"
]/g, function(ch) {
    return"&" + ntable[ch] +";";
  })
  s = s.replace(/[^ -\x7e]/g, function(ch) {
    return"&#" + ch.charCodeAt(0).toString() +";";
  });
  return s;
}

结果不会对撇号进行编码,而是对其他HTML特殊内容以及0x20-0x7e范围之外的任何字符进行编码。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var htmlEnDeCode = (function() {
    var charToEntityRegex,
        entityToCharRegex,
        charToEntity,
        entityToChar;

    function resetCharacterEntities() {
        charToEntity = {};
        entityToChar = {};
        // add the default set
        addCharacterEntities({
            '&amp;'     :   '&',
            '&gt;'      :   '>',
            '&lt;'      :   '<',
            '&quot;'    :   '"',
            '&#39;'     :  "'"
        });
    }

    function addCharacterEntities(newEntities) {
        var charKeys = [],
            entityKeys = [],
            key, echar;
        for (key in newEntities) {
            echar = newEntities[key];
            entityToChar[key] = echar;
            charToEntity[echar] = key;
            charKeys.push(echar);
            entityKeys.push(key);
        }
        charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
        entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
    }

    function htmlEncode(value){
        var htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        };

        return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
    }

    function htmlDecode(value) {
        var htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        };

        return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
    }

    resetCharacterEntities();

    return {
        htmlEncode: htmlEncode,
        htmlDecode: htmlDecode
    };
})();

这是来自ExtJS的源代码。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function encodeHTML(str) {
    return document.createElement("a").appendChild(
        document.createTextNode(str)).parentNode.innerHTML;
};

function decodeHTML(str) {
    var element = document.createElement("a");
    element.innerHTML = str;
    return element.textContent;
};
var str ="<"
var enc = encodeHTML(str);
var dec = decodeHTML(enc);
console.log("str:" + str,"
enc:"
+ enc,"
dec:"
+ dec);


选择escapeHTML()在prototype.js中所做的事情

添加此脚本可帮助您转义HTML:

1
2
3
String.prototype.escapeHTML = function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
}

现在你可以在脚本中的字符串上调用escapeHTML方法,例如:

1
2
var escapedString ="this is HTML".escapeHTML();
// gives:"&lt;h1&gt;this is HTML&lt;/h1&gt;"

希望它能帮助任何寻找简单解决方案的人,而不必包含整个prototype.js


在这里使用其他一些答案,我创建了一个版本,可以替换一次传递中的所有相关字符,而不管不同编码字符的数量(只有一次调用replace()),因此对于较大的字符串会更快。

它不依赖于DOM API存在或其他库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window.encodeHTML = (function() {
    function escapeRegex(s) {
        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    }
    var encodings = {
        '&'  : '&amp;',
        '"'  : '&quot;',
        '\'' : '&#39;',
        '<'  : '&lt;',
        '>'  : '&gt;',
        '\' : '&#x2F;'
    };
    function encode(what) { return encodings[what]; };
    var specialChars = new RegExp('
[' +
        escapeRegex(Object.keys(encodings).join('
')) +
    '
]', 'g');

    return function(text) { return text.replace(specialChars, encode); };
})();

跑了一次,你现在可以打电话了

1
encodeHTML('<>&"\'')

获得<>&"'