Assainir les entrées de l'utilisateur avant de les ajouter au DOM en Javascript

j'écris le JS pour une application de chat sur laquelle je travaille dans mon temps libre, et j'ai besoin d'avoir des identificateurs HTML qui changent en fonction des données soumises par l'utilisateur. C'est habituellement quelque chose de conceptuellement assez fragile que je ne tenterais même pas, mais je ne me vois pas avoir beaucoup de choix cette fois. Ce que je dois faire alors est d'échapper à L'id HTML pour s'assurer qu'il ne permettra pas de XSS ou de casser HTML.

Voici le code:

var user_id = escape(id)
var txt = '<div class="chut">'+
            '<div class="log" id="chut_'+user_id+'"></div>'+
            '<textarea id="chut_'+user_id+'_msg"></textarea>'+
            '<label for="chut_'+user_id+'_to">To:</label>'+
            '<input type="text" id="chut_'+user_id+'_to" value='+user_id+' readonly="readonly" />'+
            '<input type="submit" id="chut_'+user_id+'_send" value="Message"/>'+
          '</div>';

quel serait le meilleur moyen d'échapper à id pour éviter tout problème mentionné ci-dessus? Comme vous pouvez le voir, en ce moment j'utilise le intégré escape() fonction, mais je ne suis pas sûr de la qualité que cela est censé être comparé à d'autres alternatives. Je suis surtout habitué à épurer les entrées avant qu'elles ne passent dans un noeud de texte, pas un id lui-même.

29
demandé sur sth 2010-05-08 16:59:11

6 réponses

Jamais utiliser escape(). Ça n'a rien à voir avec L'encodage HTML. C'est plus comme un encodage D'URL, mais ce n'est pas vraiment ça. C'est un encodage non standard bizarre disponible seulement en JavaScript.

si vous voulez un encodeur HTML, vous devrez l'écrire vous-même car JavaScript ne vous en donne pas un. Par exemple:

function encodeHTML(s) {
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}

cependant pendant que ceci est assez pour mettre votre user_id dans des endroits comme le input value, ce n'est pas assez pour id parce que IDs ne peut utiliser qu'une sélection limitée de caractères. (Et % n'est pas parmi eux, de sorte que escape() ou encore encodeURIComponent() n'est pas bon.)

vous pouvez inventer votre propre schéma d'encodage pour mettre n'importe quel caractère dans un ID, par exemple:

function encodeID(s) {
    if (s==='') return '_';
    return s.replace(/[^a-zA-Z0-9.-]/g, function(match) {
        return '_'+match[0].charCodeAt(0).toString(16)+'_';
    });
}

Mais vous avez encore un problème si le même user_id se produit deux fois. Et pour être honnête, l'ensemble de la chose avec jeter autour des chaînes HTML est généralement une mauvaise idée. Utilisez plutôt les méthodes DOM, et conservez les références JavaScript pour chaque élément, de sorte que vous n'avez pas à continuer à appeler getElementById, ou s'inquiéter de la façon dont les chaînes arbitraires sont insérées dans les IDs.

par exemple.:

function addChut(user_id) {
    var log= document.createElement('div');
    log.className= 'log';
    var textarea= document.createElement('textarea');
    var input= document.createElement('input');
    input.value= user_id;
    input.readonly= True;
    var button= document.createElement('input');
    button.type= 'button';
    button.value= 'Message';

    var chut= document.createElement('div');
    chut.className= 'chut';
    chut.appendChild(log);
    chut.appendChild(textarea);
    chut.appendChild(input);
    chut.appendChild(button);
    document.getElementById('chuts').appendChild(chut);

    button.onclick= function() {
        alert('Send '+textarea.value+' to '+user_id);
    };

    return chut;
}

vous pouvez également utiliser une fonction de convenance ou un framework JS pour réduire la longueur des appels de create-set-appends.

ETA:

j'utilise jQuery pour le moment comme un cadre

d'accord, alors considérez les raccourcis de création jQuery 1.4, par exemple.:

var log= $('<div>', {className: 'log'});
var input= $('<input>', {readOnly: true, val: user_id});
...

le problème que j'ai maintenant est que J'utilise JSONP pour ajouter des éléments et des événements à une page, et donc je ne peux pas savoir si les éléments existent déjà ou pas avant de montrer un message.

Vous pouvez garder une recherche de user_id pour les nœuds d'élément (ou le papier d'emballage des objets) en JavaScript, pour éviter de mettre cette information dans les DOM, où les caractères qui peuvent aller dans un id sont limités.

var chut_lookup= {};
...

function getChut(user_id) {
    var key= '_map_'+user_id;
    if (key in chut_lookup)
        return chut_lookup[key];
    return chut_lookup[key]= addChut(user_id);
}

(_map_ le préfixe est parce que les objets JavaScript ne sont pas tout à fait fonctionne comme un mappage de chaînes arbitraires. La chaîne vide, et, dans IE, certains Object noms des membres, le confondre.)

34
répondu bobince 2010-05-08 16:32:52

une Autre approche que j'aime, c'est d'utiliser le natif DOM capacités: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript

15
répondu codecraig 2012-12-07 11:28:21

Vous pouvez utiliser une simple expression régulière pour affirmer que l'id ne contient que des caractères autorisés, comme:

if(id.match(/^[0-9a-zA-Z]{1,16}$/)){
    //The id is fine
}
else{
    //The id is illegal
}

Mon exemple permet de seuls les caractères alphanumériques et les chaînes de longueur 1 à 16, vous devez le modifier pour l'adapter au type d'identifiants que vous utilisez.

d'ailleurs, à la ligne 6, la propriété value manque une paire de guillemets, une erreur facile à faire quand vous citez sur deux niveaux.

Je ne vois pas votre flux de données réel, selon le contexte cette case ne doit pas être nécessaire, ou il peut ne pas être suffisant. Afin de faire un bon examen de la sécurité, nous aurions besoin de plus d'informations.

en général, à propos des fonctions d'évasion ou d'assainissement, ne leur faites pas confiance aveuglément. Vous devez savoir exactement ce qu'ils font, et vous devez établir que c'est réellement ce dont vous avez besoin. Si ce n'est pas ce dont vous avez besoin, votre propre code, la plupart du temps une simple liste blanche regex comme celui que je vous ai donné fonctionne très bien.

8
répondu aaaaaaaaaaaa 2010-05-08 14:00:50

Vous pouvez également utiliser ceci:

function sanitarize(string) {
  const map = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#x27;',
      "/": '&#x2F;',
  };
  const reg = /[&<>"'/]/ig;
  return string.replace(reg, (match)=>(map[match]));
}

la documentation de L'OWASP suggère de cartographier: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

5
répondu SilentImp 2018-01-12 13:02:31

puisque le texte que vous tentez d'échapper apparaîtra dans un attribut HTML, vous devez vous assurer d'échapper non seulement aux entités HTML, mais aussi aux attributs HTML:

var ESC_MAP = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
};

function escapeHTML(s, forAttribute) {
    return s.replace(forAttribute ? /[&<>'"]/g : /[&<>]/g, function(c) {
        return ESC_MAP[c];
    });
}

alors, votre code d'évasion devient var user_id = escapeHTML(id, true).

Pour plus d'informations, voir HTML infaillible échappant en Javascript.

2
répondu Brandon Mintern 2016-07-06 23:35:02

vous devez prendre des précautions supplémentaires lorsque vous utilisez des données fournies par l'utilisateur dans les attributs HTML. Parce que les attributs ont beaucoup plus de vecteurs d'attaque que la sortie à l'intérieur des balises HTML.

la seule façon d'éviter les attaques XSS est de tout encoder sauf les caractères alphanumériques. Escape tous les caractères avec des valeurs ASCII inférieures à 256 avec le format &#xHH;. Qui malheureusement peut causer des problèmes dans votre scénario, si vous utilisez les classes CSS et javascript pour aller chercher ces élément.

OWASP a une bonne description de la façon d'atténuer l'attribut HTML XSS:

http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.233_-_JavaScript_Escape_Before_Inserting_untrusted_data_into_html_javascript_data_values

1
répondu kozmic 2010-06-22 19:38:47