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


745

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

例如,

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

被拉入

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

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

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

问题是,当我chalk &amp; cheese从隐藏字段读取内容时,JavaScript似乎丢失了编码。我不希望值是chalk & cheese。我希望amp;保留原义。

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


可以显示您正在使用的Javascript吗?
思南大福09年

1
增加了我是如何从隐藏字段获得价值
AJM

5
请勿使用innerHTML方法(jQuery .html()方法使用innerHTML),因为在某些(我仅测试过Chrome的)浏览器中,这不会转义引号,因此,如果要将值放入属性值中,最终将导致XSS漏洞。
詹姆士·罗珀

21
在什么情况下是chalkcheese曾经一起使用0_o
d -_- b

2
比较两个项目时,@ d -_- b。例。它们就像粉笔和奶酪一样;)
Anurag 2014年

Answers:


1067

编辑:此答案发布很久以前,并且该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();
}

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

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

在此处查看运行示例。


95
这适用于大多数情况,但是htmlDecode的此实现将消除任何多余的空格。因此,对于“输入”的某些值,请输入!= htmlDecode(htmlEncode(input))。在某些情况下,这对我们来说是个问题。例如,如果输入=“ <p> \ t Hi \ n There </ p>”,则往返编码/解码将产生“ <p> Hi There </ p>”。在大多数情况下,这是可以的,但有时不是。:)
小资

7
感谢您的解决方案!我解决了消除多余的空白问题,方法是在文本值中用%% NL %%替换新行,然后调用.html()以获取HTML编码值,然后用<br />替换%% NL %% s ...不是防弹的,但是可以用,我的用户不太可能输入%% NL %%。
benno 2011年

1
有趣的是CSS具有一个white-space属性,该属性建议应如何处理HTML内容中的空格。该属性的存在暗示着“这是预先格式化的,应该保留空格和换行符”。这打破了样式和内容的分离,因为如果您尝试将HTML重新格式化为“漂亮”,或者通过这样的编码/解码周期来回移动它,那么将减少空格/间断的运行,并且编码器没有知道这样做是否可行的方法,因为它不知道white-space:pre-*;外部CSS文件中的指示器!
Triynko 2011年

2
此解决方案可能取决于页面是用html还是xhtml编写的,因此我希望使用不涉及DOM的解决方案。
Phil H

30
尽管两年后得到了答复,但下面的@Anentropic的回复在各个方面都更好。
2012年

559

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年3月4日更新:我刚刚注意到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;');

3
你也可以使用&apos;,而不是&#39;
费鲁乔


5
谢谢,我从未意识到这&apos;不是有效的HTML实体。
Ferruccio 2012年

10
不使用/g.replace()将仅替换第一个匹配项。
ThinkingStiff

1
@ Tracker1我不同意,如果该函数收到无效输入,则应引发错误。如果在特定的用例中,您想以这种方式处理无效的输入,则可以在调用函数之前检查该值,或者将该函数调用包装在try / catch中。
Anentropic

80

这是一个非jQuery版本,比jQuery .html()版本和jQuery 版本都快得多.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>

17
这就引出了一个问题:为什么它已经不是JS中的全局函数了?
SEoF

2
.replace()@SEoF最近建议的非正则表达式版本要快得多:jsperf.com/htmlencoderegex/22
Anentropic 2013年

@Anentropic确实确实可以快速点亮,但是我认为它没有用。没有/g.replace()则仅进行第一个比赛。
ThinkingStiff

有趣的是,您可以在Firefox中执行replace('a', 'b', 'g')replace(/a/g, 'b')...相同的工作,但速度也相同
Anentropic 2013年

1
我都不是:)我开始只是想处理引号,而我最终还是追求速度...
Anentropic 2013年

32

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

function multiLineHtmlEncode(value) {
    var lines = value.split(/\r\n|\r|\n/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('\r\n');
}

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


12

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

jQuery("<div/>").text(value).html is not a function

要么

Uncaught TypeError: Object has no method 'html'

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

function htmlEncode(value){ 
    if (value) {
        return jQuery('<div/>').text(value).html(); 
    } else {
        return '';
    }
}

8
jQuery('<div/>').text(value || '').html()
roufamatic 2011年

3
@roufamatic-很好的一线。但是要检查是否有非空值valueif必须立即创建DIV并获取其价值。如果htmlEncode被调用很多,并且很可能value是空的,那么它的性能会更高。
leepower 2011年

嗨,它不对&beta做β知道为什么吗?
Dilip Rajkumar

11

对于那些喜欢纯JavaScript的人,这是我成功使用的方法:

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

6

FWIW,编码不会丢失。页面加载期间,标记解析器(浏览器)使用该编码。读取并解析源​​并将浏览器将DOM加载到内存后,编码便已解析为它所表示的内容。因此,当您的JS执行以读取内存中的任何内容时,它得到的字符就是编码所表示的字符。

我可能在这里严格按照语义进行操作,但是我希望您了解编码的目的。“丢失”一词听起来好像有些东西没有正常工作。


6

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

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

或只针对主要字符担心(&,inebreaks,<,>,“和'),例如:

function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!\n\n"Safe" escape <script id=\'\'> & useful in <pre> tags!');

testing.innerHTML=test.value;

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

<div id="testing">www.WHAK.com</div>


5

原型具有内置的String类。因此,如果您正在使用/计划使用原型,它会执行以下操作:

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

9
看完Prototype的解决方案之后,这就是它的全部工作... .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); 足够简单。
史蒂夫·沃瑟姆

5
它也不应该用引号做些什么吗?那不好
Anentropic 2011年

@Anentropic我不明白为什么它需要对引号做任何事情;因为引号不需要转义,除非它们位于属性值之内。
安迪

好了,经过一番思考之后,我回想一下该评论-如果您要构建一段HTML,则希望对其中的每个部分进行编码,包括属性值,因此我同意Anentropic的观点,我认为Prototypejs函数不足以实现这种情况。
安迪

4

这是一个简单的javascript解决方案。它使用方法“ HTMLEncode”扩展了String对象,该方法可用于不带参数或带参数的对象。

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方法”


3

基于angular的清理 ...(ES6模块语法)

// 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};

虽然我确实很喜欢这个答案,但实际上我认为这是个好方法,但我怀疑是if (value === null | value === undefined) return '';错字还是按位运算符?如果是这样,为什么要使用那个而不是普通的||?谢谢!!
亚历杭德罗·瓦雷斯

1
@AlejandroVales我很确定这是一个错字...已更正。
Tracker1

1
好吧,请记住,会导致0或1,所以实际上它确实起作用了^^
Alejandro Vales

你不能只用== null吗?undefined是唯一与等价的东西null,因此无论如何都不需要两个
三等式

那根本不是真的。null而且0都是虚假的,是的,所以你不能做!value,但是重点==是使某些事情变得容易。0 == null是错误的。undefined == null是真的。您可以做value == null
Hashbrown

3

据我所知,javascript中没有任何直接的HTML编码/解码方法。

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

假设使用jQuery,这应该有效:

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

或类似的东西。


考虑到这个答案几乎与拥有870多个赞成票并且在此之后发表了一点赞成票的答案几乎相同,因此我觉得该赞成票有些有趣。
Ken Egozi '16

2

您无需转义/编码值即可将它们从一个输入字段传送到另一个输入字段。

<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>
<script>
    $(document).ready(function(e) {
        $('#button').click(function(e) {
            $('#output').val($('#hiddenId').val());
        });
    });
</script>

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

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

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

2

我遇到了类似的问题,并使用encodeURIComponentJavaScript(文档)中

例如,如果您使用的是:

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

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

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

就我而言,我必须对一个反斜杠进行编码,并且此代码可以完美地运行

encodeURIComponent('name/surname')

我得到了 name%2Fsurname


2

以下是一些Server.HTMLEncode用纯JavaScript编写的来自Microsoft ASP 的功能的仿真:

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范围以外的任何字符进行编码。


2

我的纯JS功能:

/**
 * 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实体编码和解码


1

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

http://www.jquerysdk.com/api/jQuery.html特殊字符

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

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

编辑:也有这,这是jQuery原型字符串实用程序的端口:

http://stilldesigning.com/dotstring/


1
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
<script>
String.prototype.htmlEncode = function () {
    return String(this)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');

}

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

将输出: &lt;script&gt;alert(&quot;I hack your site&quot;)&lt;/script&gt;

定义后,即可在所有字符串上访问.htmlEncode()。


1

HTML对给定值进行编码

  var htmlEncodeContainer = $('<div />');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }


0

选择escapeHTML()在prototype.js中正在做什么

添加此脚本可以帮助您逃脱HTML:

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

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

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

希望它可以帮助寻求简单解决方案的任何人,而不必包括整个prototype.js


0

在这里使用其他一些答案,我制作了一个版本,该版本一次通过替换了所有相关字符,而不考虑不同编码字符的数量(仅一次调用 replace()),因此对于较大的字符串将更快。

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

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); };
})();

跑完一次,您现在可以打电话

encodeHTML('<>&"\'')

要得到 &lt;&gt;&amp;&quot;&#39;


0

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, "\nenc: " + enc, "\ndec: " + dec);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.