如何在HTML属性值中转义引号?

我正在build立一个插入表使用jQuery创build一个HTMLstring,即

var row = ""; row += "<tr>"; row += "<td>Name</td>"; row += "<td><input value='"+data.name+"'/></td>"; row += "</tr>"; 

data.name是从一个可以包含任何字符的ajax调用返回的string。 如果它包含单引号' ,它将通过定义属性值的结尾来破坏HTML。

如何确保string在浏览器中正确呈现?

你只需要把所有'字符换成等价的HTML实体字符码:

 data.name.replace(/'/g, "&#39;"); 

或者,您可以使用jQuery的DOM操作方法创build整个事物:

 var row = $("<tr>").append("<td>Name</td><td></td>"); $("<input>", { value: data.name }).appendTo(row.children("td:eq(1)")); 

其实你可能需要这两个function之一(这取决于使用的上下文)。 这些函数处理所有types的string引号,并保护HTML / XML语法。

1.用于将文本embeddedHTML / XML的quoteattr()函数:

在上下文中使用quoteattr()函数,其结果不会被javascript评估,但必须由XML或HTMLparsing器parsing,并且必须绝对避免破坏元素属性的语法。

如果生成文本元素的内容,则换行符会自然保留。 然而,如果你正在生成一个属性的值,这个指定的值一旦被设置就会被DOM规范化,所以所有的空格(SPACE,TAB,CR,LF)都将被压缩,剥掉前导和尾随的空格并将所有中间空白序列缩减为一个空格。

但有一个例外:CR字符将被保留, 被视为空白, 只有当它用数字字符引用表示! 对于所有元素属性,结果都是有效的,NMTOKEN或IDtypes的属性或NMTOKENS除外:存在引用的CR会使这些属性的赋值无效(例如id =“…” HTML元素的属性):这个值是无效的,DOM会被忽略。 但是在其他属性(CDATAtypes)中,由数字字符引用表示的所有CR字符将被保留并且不被标准化。 请注意,这个技巧对于保留其他空格(SPACE,TAB,LF)是不行的,即使它们是由NCR表示的,因为在所有属性中,所有空格的规范化(除了NCR到CR)是强制性的。

请注意,此函数本身不执行任何HTML / XML规范化的空格,所以它在生成文本元素的内容时保持安全(在这种情况下不要传递第二个preserveCR参数)。

所以如果你传递一个可选的第二个参数(其默认值将被视为false),如果该参数的计算结果为true,那么当你想产生一个文字属性值时,使用这个NCR保留新行,这个属性是typesCDATA(例如标题=“…”属性),而不是typesID,IDLIST,NMTOKEN或NMTOKENS(例如id =“…”属性)。

 function quoteattr(s, preserveCR) { preserveCR = preserveCR ? '&#13;' : '\n'; return ('' + s) /* Forces the conversion to string. */ .replace(/&/g, '&amp;') /* This MUST be the 1st replacement. */ .replace(/'/g, '&apos;') /* The 4 other predefined entities, required. */ .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') /* You may add other replacements here for HTML only (but it's not necessary). Or for XML, only if the named entities are defined in its DTD. */ .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ .replace(/[\r\n]/g, preserveCR); ; } 

警告! 此函数仍然不检查源string(在Javascript中,它是一个16位代码单元的无限制stream),因为它在文件中的有效性必须是有效的纯文本源,并且也是HTML / XML文档。

  • 应该更新以检测和拒绝(通过例外):
    • 代表分配给非字符(如\ uFFFE和\ uFFFF)的代码点的任何代码单元:这是仅用于有效纯文本的Unicode要求;
    • 任何代理代码单元被错误地配对以形成UTF-16编码代码点的有效配对:这是有效纯文本的Unicode要求;
    • 任何有效的代理代码单元对代表在补充平面中有效的Unicode代码点,但被分配给非字符(如U + 10FFFE或U + 10FFFF):这是仅用于有效纯文本的Unicode要求;
    • 大多数C0和C1控件(除了TAB和换行符之外,在\ u0000,\ u1F和\ u007F ..范围内):这不是Unicode要求,而是对有效HTML / XML的额外要求。
  • 尽pipe有这个限制,上面的代码几乎是你想要做的。 一般。 现代JavaScript引擎应该在默认的系统对象中原生地提供这个function,但是在大多数情况下,它并不能完全保证严格的纯文本有效性,而不是HTML / XML的有效性。 但是你的Javascript代码将被调用的HTML / XML文档对象应该重新定义这个本地函数。
  • 这种限制在大多数情况下通常不是问题,因为源string是源HTML或XML DOM来源的string计算的结果。
  • 但是,如果JavaScript提取子string和中断代理对,或者它从计算数字源生成文本(将任何16位代码值转换为包含该单一代码单元的string,并附加这些短string或插入这些短string通过replace操作):如果您尝试将编码的string插入到HTML / XML DOM文本元素或HTML / XML属性值或元素名称中,则DOM本身会拒绝此插入操作,并将引发exception; 如果您的javascript插入结果string在本地二进制文件或通过二进制networking套接字发送,将不会抛出exception抛出。 这样的非纯文本string也可能是从二进制文件(如PNG,GIF或JPEG图像文件)中读取的结果,或者是从二进制安全的networking套接字(例如,而不仅仅是8位单元:大多数二进制I / Ostream是以字节为基础的,文本I / Ostream需要指定一个字符集来将文件解码为纯文本,以便在文件中find无效的编码文本stream将在您的脚本中引发I / Oexception)。

注意,这个函数,它的实现方式(如果它被扩展来纠正上面警告中提到的限制),也可以被安全地使用,并引用HTML / XML中的文本文本元素的内容(以避免离开源string值中的一些可解释的HTML / XML元素),而不仅仅是字面属性值的内容! 所以应该更好的命名为quoteml() ; 名字quoteattr()只保留传统。

你的例子就是这种情况:

 data.value = "It's just a \"sample\" <test>.\n\tTry & see yourself!"; var row = ''; row += '<tr>'; row += '<td>Name</td>'; row += '<td><input value="' + quoteattr(data.value) + '" /></td>'; row += '</tr>'; 

quoteattr() ,只使用DOM API:

或者,如果您生成的HTML代码将成为当前HTML文档的一部分,则使用文档的DOM方法单独创build每个HTML元素,以便您可以直接通过DOM API设置其属性值,而不是使用单个元素的innerHTML属性插入完整的HTML内容:

 data.value = "It's just a \"sample\" <test>.\n\tTry & see yourself!"; var row = document.createElement('tr'); var cell = document.createElement('td'); cell.innerText = 'Name'; row.appendChild(cell); cell = document.createElement('td'); var input = document.createElement('input'); input.setAttribute('value', data.value); cell.appendChild(input); tr.appendChild(cell); /* The HTML code is generated automatically and is now accessible in the row.innerHTML property, which you are not required to insert in the current document. But you can continue by appending tr into a 'tbody' element object, and then insert this into a new 'table' element object, which ou can append or insert as a child of a DOM object of your document. */ 

请注意,此替代方法不会尝试保留data.value中存在的换行符,因为您正在生成文本元素的内容,而不是此处的属性值。 如果您真的想要使用&#13;生成保留换行符的属性值&#13; ,请参阅第1部分的开头以及上面的quoteattr()的代码。

2.用于embedded到javascript / JSON文字string中的escape()函数:

在其他情况下,当意图引用一个string时,您将使用下面的escape()函数,该string将成为生成的JavaScript代码片段的一部分 ,您也希望保留该string(也可以select首先parsing可以插入更大的JavaScript代码的HTML / XMLparsing器):

 function escape(s) { return ('' + s) /* Forces the conversion to string. */ .replace(/\\/g, '\\\\') /* This MUST be the 1st replacement. */ .replace(/\t/g, '\\t') /* These 2 replacements protect whitespaces. */ .replace(/\n/g, '\\n') .replace(/\u00A0/g, '\\u00A0') /* Useful but not absolutely necessary. */ .replace(/&/g, '\\x26') /* These 5 replacements protect from HTML/XML. */ .replace(/'/g, '\\x27') .replace(/"/g, '\\x22') .replace(/</g, '\\x3C') .replace(/>/g, '\\x3E') ; } 

警告! 此源代码不会检查编码文档的有效性作为有效的纯文本文档。 但是,它不应该引发exception(内存不足情况除外):Javascript / JSON源string只是16位代码单元的不受限制的stream,不需要是纯文本格式或不受HTML / XML文档限制句法。 这意味着代码是不完整的,也应该取代:

  • 代表C0和C1控制的所有其他代码单元(除TAB和LF之外,在上面处理,但可以保持原样而不替代它们)使用\ xNN符号;
  • 所有分配给Unicode中非字符的代码单元,应该使用\ uNNNN表示法(例如\ uFFFE或\ uFFFF)replace;
  • 所有代码单元都可用作范围为\ uD800 .. \ DFFF的Unicode代理,如下所示:
    • 如果它们没有被正确地配对成代表完整范围U + 0000..U + 10FFFF中的有效Unicode代码点的有效UTF-16对,则这些替代代码单元应该分别使用符号\ uDNNNreplace;
    • 否则,如果代码单元对代表的代码点在Unicode明文中是无效的,由于代码点被分配给一个非字符,所以这两个代码点应该用符号\ U00NNNNNN来代替;
  • 最后,如果代码单元代表的代码点(或代表补充平面中的代码点的代码单元对)独立于代码点被分配或保留/未分配,在HTML / XML源文档中也是无效的(见规范),代码点应该用\ uNNNN符号(如果代码点在BMP中)或\ u00NNNNNN(如果代码点在一个辅助平面中)replace;

还要注意,最后5次replace并不是真的有必要。 但是它不包括它们,在某些情况下,有时候需要使用<![CDATA[ ... ]]>兼容性“hack”,例如进一步在HTML或XML中包含生成的javascript在<script>...</script> HTML元素中使用这个“hack”的例子如下)。

escape()函数具有插入任何HTML / XML字符引用的优点,结果将首先由Javascript解释,并且稍后将在运行时保留确切的string长度,当结果string将由JavaScript引擎评估。 它可以帮助您避免在整个应用程序代码中pipe理混合的上下文 (请参阅最后一节以及相关的安全考虑)。 值得注意的是,如果在这个上下文中使用了quoteattr() ,稍后评估和执行的javascript将不得不显式处理字符引用来重新编码它们,这是不合适的。 用例包括:

  1. 当被replace的string将被插入到生成的JavaScript事件处理程序中,其中包含一些其他HTML代码,其中JavaScript片段将包含文字引号包围的属性)。
  2. 当被replace的string将成为一个settimeout()参数的一部分,这个参数稍后将由JavaScript引擎进行eval()编辑。

示例1(仅生成JavaScript,不生成HTML内容):

 var title = "It's a \"title\"!"; var msg = "Both strings contain \"quotes\" & 'apostrophes'..."; setTimeout( '__forceCloseDialog("myDialog", "' + escape(title) + '", "' + escape(msg) + '")', 2000); 

例2(生成有效的HTML):

 var msg = "It's just a \"sample\" <test>.\n\tTry & see yourself!"; /* This is similar to the above, but this JavaScript code will be reinserted below: */ var scriptCode = 'alert("' + escape(msg) + /* important here!, because part of a JS string literal */ '");'; /* First case (simple when inserting in a text element): */ document.write( '<script type="text/javascript">' + '\n//<![CDATA[\n' + /* (not really necessary but improves compatibility) */ scriptCode + '\n//]]>\n' + /* (not really necessary but improves compatibility) */ '</script>'); /* Second case (more complex when inserting in an HTML attribute value): */ document.write( '<span onclick="' + quoteattr(scriptCode) + /* important here, because part of an HTML attribute */ '">Click here !</span>'); 

在第二个示例中,您会看到两个编码函数都同时用于嵌套在JavasSript文字中的生成文本部分(使用escape() ),生成的JavaScript代码(包含生成的string文本)本身被embedded再次使用quoteattr()重新编码,因为JavaScript代码插入HTML属性中(在第二种情况下)。

3.将文本安全编码embedded到语法上的一般注意事项:

所以总之,

  • 在生成HTML / XML属性文字quotattr()时,必须使用quotattr()函数,其中在连接中外部添加外围引号以生成完整的HTML / XML代码。
  • 在生成JavaScriptstring常量字面量的内容时,必须使用escape()函数,其中将外围的引号添加到串联的外部以生成完整的HTML / XML代码。
  • 如果仔细地使用,并且在任何地方都可以findvariables内容,以便安全地插入到另一个上下文中,并且仅在这些规则下(具有与上述完全相同的function,这些function在上下文中使用“特殊字符”),多次转义,并且转换仍然是安全的,并且将不需要附加的代码来在使用这些文字的应用程序中解码它们。 不要使用这些function。

这些函数只在那些严格的上下文中才是安全的(例如, 只有 quoteattr() HTML / XML属性值,并且只有 escape() Javascriptstring文字)。

还有其他使用不同的引用和转义机制的上下文(例如SQLstring文字,或Visual Basicstring文字,或正则expression式文本,或CSV数据文件的文本字段,或MIME标头值), 每个都需要使用自己独特的转义函数只有在这些情况下:

  • 在首先检查之前,不要假设quoteattr()escape()将是安全的,或者将不会改变转义string的语义,从而本地理解和支持(分别)HTML / XML属性值或JavaScriptstring的语法在这些情况下。
  • 例如,由escape()生成的Javascriptstring文字的语法在Java编程源代码中使用的string文字的两个其他上下文或JSON数据中的文本值中也是合适的且本地支持的。

但是相反情况并不总是如此。 例如:

  • 解释最初为Javascriptstring文字(包括例如PHP源代码中的string文字)生成的编码转义文字对于直接用作Javascript文字并不总是安全的。 通过javascript eval()系统函数来解码那些使用escape()不能转义的string文字,因为那些其他string文字可能包含其他特殊字符,这些字符会被其他的初始上下文特定地生成,而这些字符将被Javascript错误地解释,可以包含额外的转义,例如“ \Uxxxxxxxx ”或“ \e ”或“ ${var} ”和“ $$ ”,或包含其他连接运算符,例如更改引用样式的' + "可以在不同的仅支持多种转义语法的复杂环境中find并且安全:“ <!-- ”和“ --> ”或“ <[DATA[ ”和“ ]]> ”请参阅本节最后一段关于混合上下文的内容 )。
  • 这同样适用于最初为其他上下文生成的编码转义文字的解释/解码,即使用其标准文本表示forms创build的文档中的HTML / XML属性值(例如,试图解释为了embedded而生成的string文字HTML / XML文档的非标准二进制格式表示!)
  • 这也适用于javascript函数eval()的解释/解码,只使用quotteattr()安全地生成包含在HTML / XML属性文字中的string文字,这是不安全的,因为上下文被错误地混合。
  • 这也适用于解释/解码属性值文字的HTML / XML文本文件parsing器,这些parsing器仅使用escape()安全地生成以包含在Javascriptstring文字中,这将不安全,因为上下文也是不正确的混合。

4.安全地解码embedded式合成文字的值:

如果要在上下文中解码或解释string文字 ,则解码的结果string值将在不改变其他上下文的情况下互换使用,而不会被改变,即所谓的混合上下文 (例如,包括:首先在HTML / XML中用string命名一些标识符dafely编码为quotteattr() ;命名一些编程variables的Javascript从string最初安全编码escape() ;等等…),你需要准备和使用一个新的转义函数(这也将检查有效性或者拒绝它,或者截断/简化/过滤它),还有一个新的解码函数(它也会仔细地避免解释有效但不安全的序列,只能被内部接受,但不能被不安全的外部源接受,这也意味着解码function,如JavaScript中的eval() 必须绝对避免解码JSON数据源,您需要使用更安全的原生JSON解码器; 原生的JSON解码器不会解释有效的Javascript序列,例如在文字expression式,运算符或像“ {$var} ”之类的序列中包含引号分隔符),从而强化这种映射的安全性!

这些关于混合上下文中的文字解码的最后考虑事项对于您的应用程序或Web服务的安全性来说绝对是至关重要的,它们只是用安全的数据传输语法进行安全编码才是安全的。 不要在编码位置和解码位置之间混合使用这些上下文,如果这些位置不属于同一个安全领域(即使在这种情况下,使用混合上下文总是非常危险的,但是在代码中精确地跟踪是非常困难的。

出于这个原因,我build议你不要在你的应用程序的任何地方使用或假定混合的上下文 :而是为单个精确的上下文编写一个安全的编码和解码函数,对解码的string值具有精确的长度和有效性规则,以及精确的长度和有效性规则编码的stringstring。 禁止这些混合的上下文:对于上下文的每一次改变 ,使用另一对匹配的编码/解码函数(该对中使用的函数取决于哪个上下文被embedded在另一上下文中;并且这对匹配的函数也是每个上下文对)。

这意味着:

  • 为了安全地解码已经使用quoteattr()初始编码的HTML / XML属性值文本,您必须“'''''''''''''''假定它已经使用其他名称实体编码,其值将取决于定义它的特定DTD。 您必须初始化HTML / XMLparsing器,以支持由quoteattr()生成的less数几个默认命名字符实体,以及可选的数字字符实体(这也是安全的:上下文: quoteattr()函数只生成其中的一部分可以生成更多这些数字字符引用,但是不能生成其他在默认DTD中未预定义的已命名字符实体)。 所有其他命名实体必须被parsing器拒绝,因为在源string文字中解码是无效的。 或者,你可以通过定义一个unquoteattr函数(它会拒绝源string中的任何文字引号,以及不受支持的命名实体)来获得更好的性能。
  • 为了安全地解码最初用escape()编码的Javascriptstring文字(或JSONstring文字),您必须使用安全的JavaScript unescape()函数,但不要使用不安全的Javascript eval()函数!

这两个相关的安全解码function的例子如下。

5.parsingembedded在HTML / XML文本元素或属性值文字中的文本的unquoteattr()函数:

 function unquoteattr(s) { /* Note: this can be implemented more efficiently by a loop searching for ampersands, from start to end of ssource string, and parsing the character(s) found immediately after after the ampersand. */ s = ('' + s); /* Forces the conversion to string type. */ /* You may optionally start by detecting CDATA sections (like `<![CDATA[` ... `]]>`), whose contents must not be reparsed by the following replacements, but separated, filtered out of the CDATA delimiters, and then concatenated into an output buffer. The following replacements are only for sections of source text found *outside* such CDATA sections, that will be concatenated in the output buffer only after all the following replacements and security checkings. This will require a loop starting here. The following code is only for the alternate sections that are not within the detected CDATA sections. */ /* Decode by reversing the initial order of replacements. */ s = s .replace(/\r\n/g, '\n') /* To do before the next replacement. */ .replace(/[\r\n]/, '\n') .replace(/&#13;&#10;/g, '\n') /* These 3 replacements keep whitespaces. */ .replace(/&#1[03];/g, '\n') .replace(/&#9;/g, '\t') .replace(/&gt;/g, '>') /* The 4 other predefined entities required. */ .replace(/&lt;/g, '<') .replace(/&quot;/g, '"') .replace(/&apos;/g, "'") ; /* You may add other replacements here for predefined HTML entities only (but it's not necessary). Or for XML, only if the named entities are defined in *your* assumed DTD. But you can add these replacements only if these entities will *not* be replaced by a string value containing *any* ampersand character. Do not decode the '&amp;' sequence here ! If you choose to support more numeric character entities, their decoded numeric value *must* be assigned characters or unassigned Unicode code points, but *not* surrogates or assigned non-characters, and *not* most C0 and C1 controls (except a few ones that are valid in HTML/XML text elements and attribute values: TAB, LF, CR, and NL='\x85'). If you find valid Unicode code points that are invalid characters for XML/HTML, this function *must* reject the source string as invalid and throw an exception. In addition, the four possible representations of newlines (CR, LF, CR+LF, or NL) *must* be decoded only as if they were '\n' (U+000A). See the XML/HTML reference specifications ! */ /* Required check for security! */ var found = /&[^;])*;?/.match(s); if (found.length >0 && found[0] != '&amp;') throw 'unsafe entity found in the attribute literal content'; /* This MUST be the last replacement. */ s = s.replace(/&amp;/g, '&'); /* The loop needed to support CDATA sections will end here. This is where you'll concatenate the replaced sections (CDATA or not), if you have splitted the source string to detect and support these CDATA sections. Note that all backslashes found in CDATA sections do NOT have the semantic of escapes, and are *safe*. On the opposite, CDATA sections not properly terminated by a matching `]]>` section terminator are *unsafe*, and must be rejected before reaching this final point. */ return s; } 

请注意,此函数不会分析用于围绕HTML属性值的周围的引号分隔符。 这个函数实际上可以解码任何HTML / XML文本元素的内容,可能包含文字引号,这是安全的 。 这是您parsingHTML代码以提取HTML / XML属性中使用的引用string的可撤销性,并在调用unquoteattr()函数之前unquoteattr()这些匹配的引号分隔符。

6.parsingembedded在Javascript / JSON文字中的文本内容的unescape()函数:

 function unescape(s) { /* Note: this can be implemented more efficiently by a loop searching for backslashes, from start to end of source string, and parsing and dispatching the character found immediately after the backslash, if it must be followed by additional characters such as an octal or hexadecimal 7-bit ASCII-only encoded character, or an hexadecimal Unicode encoded valid code point, or a valid pair of hexadecimal UTF-16-encoded code units representing a single Unicode code point. 8-bit encoded code units for non-ASCII characters should not be used, but if they are, they should be decoded into a 16-bit code units keeping their numeric value, ie like the numeric value of an equivalent Unicode code point (which means ISO 8859-1, not Windows 1252, including C1 controls). Note that Javascript or JSON does NOT require code units to be paired when they encode surrogates; and Javascript/JSON will also accept any Unicode code point in the valid range representable as UTF-16 pairs, including NULL, all controls, and code units assigned to non-characters. This means that all code points in \U00000000..\U0010FFFF are valid, as well as all 16-bit code units in \u0000..\uFFFF, in any order. It's up to your application to restrict these valid ranges if needed. */ s = ('' + s) /* Forces the conversion to string. */ /* Decode by reversing the initial order of replacements */ .replace(/\\x3E/g, '>') .replace(/\\x3C/g, '<') .replace(/\\x22/g, '"') .replace(/\\x27/g, "'") .replace(/\\x26/g, '&') /* These 5 replacements protect from HTML/XML. */ .replace(/\\u00A0/g, '\u00A0') /* Useful but not absolutely necessary. */ .replace(/\\n/g, '\n') .replace(/\\t/g, '\t') /* These 2 replacements protect whitespaces. */ ; /* You may optionally add here support for other numerical or symbolic character escapes. But you can add these replacements only if these entities will *not* be replaced by a string value containing *any* backslash character. Do not decode to any doubled backslashes here ! */ /* Required check for security! */ var found = /\\[^\\])?/.match(s); if (found.length > 0 && found[0] != '\\\\') throw 'Unsafe or unsupported escape found in the literal string content'; /* This MUST be the last replacement. */ return s.replace(/\\\\/g, '\\'); } 

请注意,此函数不会parsing用于围绕Javascript或JSONstring的周围的引号分隔符。 这是您parsingJavaScript或JSON源代码以提取带引号的string文字的可回复性,并在调用unescape()函数之前去除这些匹配的引号分隔符。

 " = &quot; or &#34; ' = &#39; 

例子:

 <div attr="Tim &quot;The Toolman&quot; Taylor" <div attr='Tim "The Toolman" Taylor' <div attr="Tim 'The Toolman' Taylor" <div attr='Tim &#39;The Toolman&#39; Taylor' 

在JavaScriptstring中,使用\来转义引号字符:

 var s = "Tim \"The Toolman\" Taylor"; var s = 'Tim \'The Toolman\' Taylor'; 

所以,引用你的属性值“,并使用这样的function:

 function escapeAttrNodeValue(value) { return value.replace(/(&)|(")|(\u00A0)/g, function(match, amp, quote) { if (amp) return "&amp;"; if (quote) return "&quot;"; return "&nbsp;"; }); } 

我想你可以这样做:

 var row = ""; row += "<tr>"; row += "<td>Name</td>"; row += "<td><input value=\""+data.name+"\"/></td>"; row += "</tr>"; 

如果你担心在现有的单引号data.name

在最好的情况下,你可以创build一个INPUT元素,然后setValue(data.name)

我的回答部分基于Andy E,我仍然推荐阅读verdy_p写下的内容,但这里是

 $("<a>", { href: 'very<script>\'b"ad' }).text('click me')[0].outerHTML 

免责声明:这不是确切的问题,而只是“如何逃避属性”

给出的答案似乎相当复杂,所以对于我的使用情况,我尝试了内置的encodeURIComponentdecodeURIComponent ,并发现它们运行良好。