Тепер, коли всі основні браузери підтримують вбудовані фреймворки, існує набагато простіший спосіб, який, на мою думку, може бути безпечним. Я б дуже хотів, щоб цю відповідь могли переглянути люди, які більше знайомі з таким видом безпеки.
ПРИМІТКА. Цей метод точно не працюватиме в IE 9 та раніше. Дивіться цю таблицю для версій браузера, які підтримують пісочницю. (Примітка: у таблиці, здається, сказано, що вона не буде працювати в Opera Mini, але я щойно спробував, і вона спрацювала.)
Ідея полягає в тому, щоб створити прихований iframe з вимкненим JavaScript, вставити в нього ваш ненадійний HTML і дати йому проаналізувати його. Потім ви можете пройти дерево DOM і скопіювати теги та атрибути, які вважаються безпечними.
Показані тут білі списки - лише приклади. Що найкраще внести в білий список, залежить від програми. Якщо вам потрібна більш складна політика, ніж просто білі списки тегів та атрибутів, це можна застосувати за допомогою цього методу, але не цього прикладу коду.
var tagWhitelist_ = {
'A': true,
'B': true,
'BODY': true,
'BR': true,
'DIV': true,
'EM': true,
'HR': true,
'I': true,
'IMG': true,
'P': true,
'SPAN': true,
'STRONG': true
};
var attributeWhitelist_ = {
'href': true,
'src': true
};
function sanitizeHtml(input) {
var iframe = document.createElement('iframe');
if (iframe['sandbox'] === undefined) {
alert('Your browser does not support sandboxed iframes. Please upgrade to a modern browser.');
return '';
}
iframe['sandbox'] = 'allow-same-origin';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.contentDocument.body.innerHTML = input;
function makeSanitizedCopy(node) {
if (node.nodeType == Node.TEXT_NODE) {
var newNode = node.cloneNode(true);
} else if (node.nodeType == Node.ELEMENT_NODE && tagWhitelist_[node.tagName]) {
newNode = iframe.contentDocument.createElement(node.tagName);
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes[i];
if (attributeWhitelist_[attr.name]) {
newNode.setAttribute(attr.name, attr.value);
}
}
for (i = 0; i < node.childNodes.length; i++) {
var subCopy = makeSanitizedCopy(node.childNodes[i]);
newNode.appendChild(subCopy, false);
}
} else {
newNode = document.createDocumentFragment();
}
return newNode;
};
var resultElement = makeSanitizedCopy(iframe.contentDocument.body);
document.body.removeChild(iframe);
return resultElement.innerHTML;
};
Ви можете спробувати тут .
Зверніть увагу, що в цьому прикладі я забороняю атрибути та теги стилю. Якщо ви дозволили їх, ви, мабуть, захочете проаналізувати CSS і переконатися, що це безпечно для ваших цілей.
Я протестував це на декількох сучасних браузерах (Chrome 40, Firefox 36 Beta, IE 11, Chrome для Android), а також на одному старому (IE 8), щоб переконатися, що він виручав перед виконанням будь-яких сценаріїв. Мені було б цікаво дізнатись, чи є браузери, які мають проблеми з цим, або випадки, які я не помічаю.