Bây giờ tất cả các trình duyệt chính đều hỗ trợ iframe hộp cát, có một cách đơn giản hơn nhiều mà tôi nghĩ có thể an toàn. Tôi rất thích nếu câu trả lời này có thể được xem xét bởi những người quen thuộc hơn với loại vấn đề bảo mật này.
LƯU Ý: Phương pháp này chắc chắn sẽ không hoạt động trong IE 9 trở về trước. Xem bảng này để biết các phiên bản trình duyệt hỗ trợ hộp cát. (Lưu ý: bảng có vẻ nói rằng nó sẽ không hoạt động trong Opera Mini, nhưng tôi vừa thử nó và nó đã hoạt động.)
Ý tưởng là tạo một iframe ẩn với JavaScript bị vô hiệu hóa, dán HTML không đáng tin cậy của bạn vào đó và để nó phân tích cú pháp. Sau đó, bạn có thể đi qua cây DOM và sao chép các thẻ và thuộc tính được coi là an toàn.
Danh sách trắng được hiển thị ở đây chỉ là ví dụ. Điều tốt nhất để đưa vào danh sách trắng sẽ phụ thuộc vào ứng dụng. Nếu bạn cần một chính sách phức tạp hơn chỉ là danh sách trắng các thẻ và thuộc tính, thì phương pháp này có thể đáp ứng được bằng phương pháp này, mặc dù không phải bằng mã ví dụ này.
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;
};
Bạn có thể dùng thử tại đây .
Lưu ý rằng tôi không cho phép các thuộc tính và thẻ kiểu trong ví dụ này. Nếu bạn cho phép chúng, bạn có thể muốn phân tích cú pháp CSS và đảm bảo rằng nó an toàn cho mục đích của bạn.
Tôi đã thử nghiệm điều này trên một số trình duyệt hiện đại (Chrome 40, Firefox 36 Beta, IE 11, Chrome dành cho Android) và trên một trình duyệt cũ (IE 8) để đảm bảo rằng nó được bảo hành trước khi thực thi bất kỳ tập lệnh nào. Tôi muốn biết liệu có bất kỳ trình duyệt nào gặp sự cố với nó hoặc bất kỳ trường hợp phức tạp nào mà tôi đang bỏ qua hay không.