在将用户输入添加到使用Javascript的DOM中之前对用户输入进行消毒
我正在为空闲时间正在编写的聊天应用程序编写JS,并且需要根据用户提交的数据更改HTML标识。通常情况下,这通常是一些不稳定的事情,我甚至不会去尝试,但是这次我没有看到自己有太多的选择。然后我需要做的就是转义HTML标识以确保它不会允许XSS或破坏HTML。在将用户输入添加到使用Javascript的DOM中之前对用户输入进行消毒
下面的代码:
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本身。
从不使用escape()
。这与HTML编码无关。这更像URL编码,但它甚至不适合。这是一个奇怪的非标准编码,只能在JavaScript中使用。
如果你想要一个HTML编码器,你必须自己编写它,因为JavaScript不会给你一个。例如:
function encodeHTML(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');
}
不过虽然这是足以让你在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框架来减少创建集附加调用的长度。
ETA:
我使用jQuery的那一刻作为一个框架
OK,然后再考虑了jQuery 1.4创建的快捷方式,例如:
var log= $('<div>', {className: 'log'});
var input= $('<input>', {readOnly: true, val: user_id});
...
我现在的问题是,我使用JSONP添加元素和事件到一个页面,所以我不知道这些元素是否已经存在或不存在之前显示一条消息。
可以保持user_id
至元素节点(或包装对象)在JavaScript中查找,保存把这些信息在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
成员名称,混淆。)
我目前使用jQuery作为框架,所以任何与此相关的想法都可能会有帮助。 我现在遇到的问题是,我使用JSONP将元素和事件添加到页面,因此在显示消息之前无法确定元素是否已存在。这让我觉得我不得不使用糟糕的方法,我必须找到要选择的元素,或者如果它们不在那里,请添加它们。因此,我不认为你最后的建议可行,但我可能是错的。 受限制的字符集使我认为该ID的MD5可能是我所能做的。 – 2010-05-08 14:26:42
尽管我可以添加关于用户名接受的字符的假设,但可以使用正则表达式并完成它。 – 2010-05-08 14:34:44
你可以使用一个简单的正则表达式断言ID只包含允许的字符,像这样:
if(id.match(/^[0-9a-zA-Z]{1,16}$/)){
//The id is fine
}
else{
//The id is illegal
}
我的例子只允许字母数字字符,长度为1的串16,你应该改变它以匹配您使用的ids类型。
顺便说一下,在第6行,value属性缺少一对引号,这是在两个级别引用时容易犯的错误。
我看不到您的实际数据流,取决于上下文,可能根本不需要此检查,或者它可能不够。为了做出适当的安全审查,我们需要更多的信息。
一般而言,关于建立在逃生或消毒功能,不要盲目信任他们。你需要确切地知道他们做了什么,并且你需要确定这实际上是你需要的。如果它不是你所需要的,那么你的代码就是你自己的,大多数情况下,像我给你的简单的白名单正则表达式工作得很好。
在HTML属性中使用用户提供的数据时,您需要采取额外的预防措施。因为属性比HTML标签中的输出具有更多的攻击向量。
避免XSS攻击的唯一方法是对除字母数字字符以外的所有内容进行编码。使用& #xHH转义ASCII值小于256的所有字符;格式。如果您使用CSS类和JavaScript来获取这些元素,那么不幸的是可能会在您的方案中导致问题。
OWASP有HTML属性XSS如何减轻一个很好的说明:
另一种方法,我喜欢的是使用原生DOM功能:http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript
@BrandonMintern的要点不适合我。 – cmcculloh 2016-07-05 18:10:12
我写了那篇博文。不幸的是,顶部给出的TL; DR技术不适用于HTML属性。还有一些其他方法可以在文章结尾正确转义HTML属性,即:http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/#hack-3-more-efficient -catchall – 2016-07-06 23:06:59
既然你是文本转义将出现在HTML属性中,您必须确保不仅逃脱HTML实体,还转义HTML属性:
var ESC_MAP = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
function escapeHTML(s, forAttribute) {
return s.replace(forAttribute ? /[&<>'"]/g : /[&<>]/g, function(c) {
return ESC_MAP[c];
});
}
然后,您的转义码变为var user_id = escapeHTML(id, true)
。
欲了解更多信息,请参阅Foolproof HTML escaping in Javascript。
你也可以使用这样的:
function sanitarize(string) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
"/": '/',
};
const reg = /[&<>"'/]/ig;
return string.replace(reg, (match)=>(map[match]));
}
OWASP文档建议马平:https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
到底是什么'id'的功能? – Tgr 2010-05-08 13:17:00
现在id是代表用户的任何字符串。我用它来区分属于不同用户的页面的类似结构。 通过这个逻辑,MD5或base64可能是一个很好的等待,我想。这只是没有内置的东西。 – 2010-05-08 13:40:47