javascript - validar - Codificación HTML perdida cuando el atributo se lee desde el campo de entrada




validar solo letras en html (16)

Estoy usando JavaScript para extraer un valor de un campo oculto y mostrarlo en un cuadro de texto. El valor en el campo oculto está codificado.

Por ejemplo,

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

se tira en

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

a través de algunos jQuery para obtener el valor del campo oculto (es en este punto que pierdo la codificación):

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

El problema es que cuando leo chalk &amp; cheese chalk &amp; cheese del campo oculto, JavaScript parece perder la codificación. Escapar " y ' , quiero que la codificación se mantenga.

¿Existe una biblioteca de JavaScript o un método jQuery que codifique en HTML una cadena?


Afaik no hay ningún método de codificación / decodificación HTML sencillo en javascript.

Sin embargo, lo que puede hacer es usar JS para crear un elemento arbitrario, configurar su texto interno y luego leerlo usando innerHTML.

digamos, con jQuery esto debería funcionar:

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

o algo por el estilo


Aquí hay un poco de lo que emula la función Server.HTMLEncode de la ASP de Microsoft, escrita en JavaScript puro:

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

El resultado no codifica apóstrofes, sino que codifica las otras ofertas especiales de HTML y cualquier carácter fuera del rango 0x20-0x7e.


Aquí hay una versión no jQuery que es considerablemente más rápida que la versión jQuery .html() y la versión .replace() . Esto conserva todo el espacio en blanco, pero al igual que la versión jQuery, no maneja las comillas.

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

Velocidad: http://jsperf.com/htmlencoderegex/17

Manifestación:

Salida:

Guión:

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>

Basado en https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435 ... (sintaxis del módulo 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};

El truco de jQuery no codifica las comillas y en IE eliminará tu espacio en blanco.

Basado en el templatetag de escape en Django, que creo que ya está muy usado / probado, hice esta función que hace lo que se necesita.

Podría decirse que es más simple (y posiblemente más rápido) que cualquiera de las soluciones para el problema del borrado de espacios en blanco, y codifica comillas, lo cual es esencial si va a usar el resultado dentro de un valor de atributo, por ejemplo.

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, '&');
}

Actualización 2013-06-17:
En la búsqueda del escape más rápido, he encontrado esta implementación de un método replaceAll :
http://dumpsite.com/forum/index.php?topic=4.msg29#msg29
(También se hace referencia aquí: el método más rápido para reemplazar todas las instancias de un carácter en una cadena )
Algunos resultados de rendimiento aquí:
http://jsperf.com/htmlencoderegex/25

Da una cadena de resultado idéntica a las cadenas de replace incorporadas arriba. Estaría muy feliz si alguien pudiera explicar por qué es más rápido.

Actualización 2015-03-04:
Acabo de notar que AngularJS está usando exactamente el método anterior:
https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435

Añaden un par de mejoras: parecen estar manejando un problema oscuro de Unicode , así como convirtiendo todos los caracteres no alfanuméricos en entidades. Tenía la impresión de que esto último no era necesario, siempre que tenga un conjunto de caracteres UTF8 especificado para su documento.

Notaré que (4 años después) Django todavía no hace ninguna de estas cosas, por lo que no estoy seguro de cuán importantes son:
https://github.com/django/django/blob/1.8b1/django/utils/html.py#L44

Actualización 2016-04-06:
También es posible que desee escapar de barra diagonal / . Esto no es necesario para la codificación HTML correcta, sin embargo, OWASP lo recomienda como una medida de seguridad anti-XSS. (Gracias a @JNF por sugerir esto en los comentarios)

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

Escoger lo que escapeHTML() está haciendo en prototype.js

Agregar este script te ayuda a escapar de HTML:

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

Ahora puede llamar al método escapeHTML en cadenas en su script, como:

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

Espero que ayude a cualquiera que busque una solución simple sin tener que incluir todo el prototype.js


HtmlEncodifica el valor dado

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

Más rápido sin Jquery. Puedes codificar cada caracter en tu cadena:

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

O simplemente apunte a los personajes principales para preocuparse (&, inebreaks, <,>, "y ') como:

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>


No debería tener que escapar / codificar valores para trasladarlos de un campo de entrada a otro.

<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 no va insertando HTML en bruto ni nada; simplemente le dice al DOM que establezca la propiedad de value (o atributo; no estoy seguro). De cualquier manera, el DOM maneja cualquier problema de codificación por usted. A menos que esté haciendo algo extraño como usar document.write o eval , la codificación HTML será efectivamente transparente.

Si estás hablando de generar un nuevo cuadro de texto para mantener el resultado ... sigue siendo tan fácil. Simplemente pase la parte estática del HTML a jQuery, y luego establezca el resto de las propiedades / atributos en el objeto que le devuelve.

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

Para aquellos que prefieren el Javascript simple, este es el método que he usado con éxito:

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

Si quieres usar jQuery. Encontré esto:

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

(parte del complemento jquery.string ofrecido por jQuery SDK)

El problema con Prototype, creo, es que extiende los objetos base en JavaScript y será incompatible con cualquier jQuery que hayas usado. Por supuesto, si ya está utilizando Prototype y no jQuery, no será un problema.

EDITAR: También está esto, que es un puerto de las utilidades de cadena de Prototype para jQuery:

http://stilldesigning.com/dotstring/


Tuve un problema similar y lo solucioné utilizando la función encodeURIComponent de JavaScript ( documentation )

Por ejemplo, en tu caso si usas:

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

y

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

chalk%20%26%20cheese . Incluso los espacios se mantienen.

En mi caso, tuve que codificar una barra invertida y este código funciona perfectamente

encodeURIComponent('name/surname')

y me dieron name%2Fsurname


Yo uso estas funciones:

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

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

Básicamente, un elemento div se crea en la memoria, pero nunca se agrega al documento.

En la función htmlEncode , establezco el texto innerText del elemento y recupero el innerText codificado; en la función htmlDecode establecí el valor innerHTML del elemento y se recupera el innerText .

Compruebe un ejemplo de ejecución here .


Prototype tiene incorporado en la clase String . Entonces, si estás usando / planeas usar Prototype, hace algo como:

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

<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>

Se mostrará: &lt;script&gt;alert(&quot;I hack your site&quot;)&lt;/script&gt;

Se podrá acceder a .htmlEncode () en todas las cadenas una vez definidas.


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

Esto es del código fuente de ExtJS.





html-escape-characters