Я пишу JS для приложения чата, я продолжаю работать в свое свободное время, и у меня должны быть идентификаторы HTML, которые изменяются согласно отправленным данным пользователя. Это обычно - что-то концептуально достаточно шаткое, что я даже не делал бы попытку его, но я не вижу, что я имею большую часть выбора на этот раз. Что я должен сделать, затем должен выйти из идентификатора HTML, чтобы удостовериться, что он не будет допускать 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()
функция, но я не уверен в том, как хороший это, как предполагается, сравнивается с другими альтернативами. Я главным образом привык к очистке входа, прежде чем это войдет в текстовый узел, не сам идентификатор.
Никогда не используйте escape ()
. Это не имеет отношения к кодировке HTML. Это больше похоже на URL-кодирование, но это даже не совсем так. Это странная нестандартная кодировка, доступная только в JavaScript.
Если вам нужен кодировщик HTML, вам придется написать его самостоятельно, поскольку в JavaScript его нет. Например:
function encodeHTML(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');
}
Однако, хотя этого достаточно, чтобы поместить ваш user_id
в такие места, как входное значение
, этого недостаточно для id
, потому что идентификаторы могут использовать только ограниченный выбор персонажей. (И %
среди них нет, поэтому escape ()
или даже encodeURIComponent ()
не годятся.)
Вы можете изобрести свою собственную кодировку. схема для вставки любых символов в идентификатор, например:
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
или беспокоиться о том, как произвольные строки вставляются в идентификаторы.
например:
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.
ETA:
В настоящее время я использую jQuery в качестве фреймворка.
Хорошо, тогда рассмотрим ярлыки создания 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
] имена участников, запутайте.)
Вы можете использовать простое регулярное выражение, чтобы утверждать, что идентификатор содержит только разрешенные символы, например:
if(id.match(/^[0-9a-zA-Z]{1,16}$/)){
//The id is fine
}
else{
//The id is illegal
}
В моем примере разрешены только буквенно-цифровые символы и строки длиной от 1 до 16, вы должны изменить его, чтобы он соответствовал типу идентификаторы, которые вы используете.
Между прочим, в строке 6 в свойстве value отсутствует пара кавычек, что легко сделать, когда вы цитируете на двух уровнях.
Я не могу увидеть ваш реальный поток данных, в зависимости от контекста эта проверка может вообще не понадобиться или ее может быть недостаточно. Чтобы провести надлежащую проверку безопасности, нам потребуется дополнительная информация.
В общем, что касается встроенных функций escape или очистки, не доверяйте им слепо. Вам нужно точно знать, что они делают, и вы должны убедиться, что это именно то, что вам нужно. Если это не то, что вам нужно, код ваш собственный, в большинстве случаев простое регулярное выражение с белым списком, подобное тому, которое я вам дал, отлично работает.
Необходимо принимать дополнительные меры предосторожности при использовании пользовательских данных в атрибутах HTML. Потому что атрибуты имеют гораздо больше векторов атак, чем вывод внутри HTML-тегов.
Единственный способ избежать XSS-атак - кодировать все, кроме буквенно-цифровых символов. Все символы со значениями ASCII менее 256 экранируйте форматом HH;. Что, к сожалению, может вызвать проблемы в вашем сценарии, если вы используете классы CSS и javascript для получения этих элементов.
У OWASP есть хорошее описание того, как смягчить последствия XSS атрибутов HTML: