2010-05-08 2 views
25

Я пишу JS для приложения чата, над которым я работаю в свободное время, и мне нужно иметь идентификаторы HTML, которые меняются в соответствии с представленными пользователем данными. Это обычно что-то концептуально шаткий, что я бы даже не попытался, но на этот раз я не вижу, чтобы у меня был большой выбор. Мне нужно сделать это, чтобы избежать идентификатора HTML, чтобы убедиться, что он не позволит XSS или взломать HTML.Санизируя ввод пользователя перед добавлением его в DOM в Javascript

Вот код:

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(), но я не уверен, насколько хорошо это должно сравниться с другими альтернативами. В основном я использую для дезинфекции входных данных, прежде чем он попадет в текстовый узел, а не сам идентификатор.

+0

Что именно является функцией 'id'? – Tgr

+0

id прямо сейчас представляет собой любую строку, представляющую пользователя.Я использую его, чтобы различать похожие структуры страницы, принадлежащие разным пользователям. по этой логике, MD5 или base64 может быть хорошим ожиданием, чтобы сделать это, я думаю. Для этого просто нет встроенного материала. –

ответ

30

Никогда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, поскольку идентификаторы можно использовать только ограниченный набор символов. (И % среди них нет, так 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 или беспокоиться о том, как вставляются произвольные строки в идентификаторы.

например.:

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 имен членов, запутать его.)

+0

Я использую jQuery на данный момент в качестве рамки, поэтому любая идея, связанная с этим, может быть полезна. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что я использую JSONP для добавления элементов и событий на страницу, поэтому я не могу знать, существуют ли элементы уже или нет, прежде чем показывать сообщение. Это заставило меня думать, что я вынужден использовать дрянной метод, я должен найти, какие элементы выбрать или добавить их, если их еще нет. Из-за этого я не думаю, что ваше последнее предложение может работать, но я могу ошибаться. Ограниченный набор символов заставляет меня думать, что MD5 идентификатора может быть всем, что я могу сделать. –

+0

Хотя я мог бы просто добавить предположение о принятых символах имени пользователя, использовать регулярные выражения и делать с ними. –

7

Вы можете использовать простое регулярное выражение, чтобы утверждать, что идентификатор содержит только допустимые символы, например:

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

Мой пример позволяет только буквенно-цифровые символы и строки длиной от 1 до 16 лет, вы должны изменить его чтобы соответствовать типу, который вы используете.

Кстати, в строке 6 свойство value отсутствует пара кавычек, что легко сделать при цитировании на двух уровнях.

Я не вижу ваш фактический поток данных, в зависимости от контекста эта проверка может вообще не понадобиться, или ее может быть недостаточно. Для обеспечения надлежащего обзора безопасности нам потребуется дополнительная информация.

В целом, о встроенных средствах спасения или дезинфекции, не доверяйте им вслепую. Вам нужно точно знать, что они делают, и вам нужно установить, что это на самом деле то, что вам нужно. Если это не то, что вам нужно, код ваш собственный, большую часть времени простой белый список, как тот, который я дал вам работает просто отлично.

1

При использовании пользовательских данных в атрибутах HTML вам необходимо принять дополнительные меры предосторожности. Поскольку атрибуты имеют гораздо больше векторов атак, чем вывод внутри HTML-тегов.

Единственный способ избежать атак XSS - это кодировать все, кроме буквенно-цифровых символов. Удалите все символы с значениями ASCII менее 256 с помощью & #xHH; формат. К сожалению, это может вызвать проблемы в вашем сценарии, если вы используете классы CSS и javascript для извлечения этих элементов.

OWASP имеет хорошее описание того, как смягчать атрибута 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

12

Другой подход, который мне нравится использовать собственные возможности DOM: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript

+1

@BrandonMintern gist не работает для меня. – cmcculloh

+1

Я написал это сообщение в блоге. К сожалению, метод TL; DR, указанный в верхней части, не подходит для использования в атрибутах HTML. Есть еще несколько подходов к концу сообщения, которые правильно выходят за атрибуты HTML, а именно: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/#hack-3-more-efficient -catchall –

1

Так как текст, который вы escaping будет отображаться в атрибуте HTML, вы должны обязательно избегать не только HTML-объектов, но и атрибутов 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]; 
    }); 
} 

Тогда ваш код экранирования будет var user_id = escapeHTML(id, true).

Для получения дополнительной информации см. Foolproof HTML escaping in Javascript.

Смежные вопросы