一尘不染

在将用户输入添加到Javascript的DOM中之前,先对其进行消毒

javascript

我正在为业余时间使用的聊天应用程序编写JS,并且需要具有根据用户提交的数据而变化的HTML标识符。从概念上讲,这通常有些不稳定,以至于我什至都不会尝试,但是这次我没有太多选择。然后,我需要做的是转义HTML
ID,以确保它不会允许XSS或破坏HTML。

这是代码:

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

id避免上述任何问题的最佳逃脱途径是什么?如您所见,现在我正在使用内置escape()函数,但不确定将其与其他替代方案相比有多好。在输入到文本节点之前,我通常习惯于对输入进行消毒,而不是id本身。


阅读 357

收藏
2020-05-01

共1个答案

一尘不染

永远不要 使用escape()。与HTML编码无关。它更像是URL编码,但那还不合适。这是一种奇怪的非标准编码,仅在JavaScript中可用。

如果您需要HTML编码器,则必须自己编写,因为JavaScript不能给您一个。例如:

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

但是,尽管这足以将您放置user_id在的位置input value,但这还不够,id因为ID只能使用有限的字符选择。(而且%不在其中,所以escape()甚至encodeURIComponent()是不好的。)

您可以发明自己的编码方案,以将任何字符放入ID中,例如:

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

但是,如果user_id两次出现相同的问题,您仍然会遇到问题。坦白地说,抛出HTML字符串的整个过程通常不是一个好主意。请改用DOM方法,并保留对每个元素的JavaScript引用,因此您不必继续调用getElementById,也不必担心如何将任意字符串插入ID中。

例如。:

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

您还可以使用便利函数或JS框架来减少其中的create-set-appends调用的冗长时间。

预计到达时间:

我目前正在使用jQuery作为框架

确定,然后考虑jQuery 1.4创建快捷方式,例如:

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

我现在遇到的问题是我使用JSONP向页面添加元素和事件,因此在显示消息之前我不知道元素是否已经存在。

您可以user_id在JavaScript中查找元素节点(或包装对象),以将该信息保存在DOM本身中,而DOM中可以包含的字符id受到限制。

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_前缀是因为JavaScript对象不能 完全 用作任意字符串的映射。空字符串以及IE中的某些Object成员名称将其混淆。)

2020-05-01