Muszę wyświetlić zasoby zewnętrzne ładowane za pośrednictwem żądań między domenami i upewnić się, że wyświetlam tylko „ bezpieczne ” treści.
Można by użyć Prototype's String # stripScripts do usunięcia bloków skryptów. Ale opiekunowie tacy jak onclick
lub onerror
nadal tam są.
Czy jest jakaś biblioteka, która może przynajmniej
- usuwanie bloków skryptów,
- zabić programy obsługi DOM,
- usuń tagi z czarnej listy (np.:
embed
lubobject
).
Czy są więc jakieś linki i przykłady związane z JavaScriptem?
javascript
html
security
html-sanitizing
aemkei
źródło
źródło
Odpowiedzi:
Aktualizacja 2016: dostępny jest teraz pakiet Google Closure oparty na środku dezynfekującym Caja.
Ma bardziej przejrzyste API, został przepisany w celu uwzględnienia API dostępnych w nowoczesnych przeglądarkach i lepiej współdziała z Closure Compiler.
Bezwstydna wtyczka: zobacz caja / plugin / html-sanitizer.js, aby zapoznać się ze środkiem dezynfekującym HTML po stronie klienta, który został dokładnie sprawdzony.
Jest na białej liście, a nie na czarnej liście, ale białe listy można konfigurować zgodnie z CajaWhitelists
Jeśli chcesz usunąć wszystkie tagi, wykonaj następujące czynności:
var tagBody = '(?:[^"\'>]|"[^"]*"|\'[^\']*\')*'; var tagOrComment = new RegExp( '<(?:' // Comment body. + '!--(?:(?:-*[^->])*--+|-?)' // Special "raw text" elements whose content should be elided. + '|script\\b' + tagBody + '>[\\s\\S]*?</script\\s*' + '|style\\b' + tagBody + '>[\\s\\S]*?</style\\s*' // Regular name + '|/?[a-z]' + tagBody + ')>', 'gi'); function removeTags(html) { var oldHtml; do { oldHtml = html; html = html.replace(tagOrComment, ''); } while (html !== oldHtml); return html.replace(/</g, '<'); }
Ludzie powiedzą ci, że możesz utworzyć element i przypisać go,
innerHTML
a następnie pobraćinnerText
lubtextContent
, a następnie uciec z tego bytów. Nie rób tego. Jest podatny na wstrzyknięcie XSS, ponieważ<img src=bogus onerror=alert(1337)>
uruchomionerror
procedurę obsługi, nawet jeśli węzeł nigdy nie jest dołączony do DOM.źródło
cssparser.js
, ale co ważniejsze,html4
obiektu). Dodatkowo zanieczyszcza globalnąwindow
własność. Czy istnieje wersja dla sieci WWW tego kodu? Jeśli nie, czy widzisz lepszy sposób na wyprodukowanie i utrzymanie takiego, niż stworzenie dla niego osobnego projektu?Środek dezynfekujący Google Caja HTML można przygotować „do pracy w sieci”, osadzając go w programie do obsługi sieci . Wszelkie zmienne globalne wprowadzone przez środek odkażający będą zawarte w pliku roboczym, a przetwarzanie będzie odbywać się w jego własnym wątku.
W przypadku przeglądarek, które nie obsługują aplikacji Web Workers, możemy użyć elementu iframe jako oddzielnego środowiska, w którym działa środek odkażający. Timothy Chien ma polyfill , który właśnie to robi, używając ramek iframe do symulacji pracowników sieci, więc ta część jest wykonywana za nas.
Projekt Caja ma stronę wiki na temat używania Caja jako samodzielnego środka odkażającego po stronie klienta :
ant
html-sanitizer-minified.js
lubhtml-css-sanitizer-minified.js
na swojej stroniehtml_sanitize(...)
Skrypt roboczy musi tylko postępować zgodnie z tymi instrukcjami:
importScripts('html-css-sanitizer-minified.js'); // or 'html-sanitizer-minified.js' var urlTransformer, nameIdClassTransformer; // customize if you need to filter URLs and/or ids/names/classes urlTransformer = nameIdClassTransformer = function(s) { return s; }; // when we receive some HTML self.onmessage = function(event) { // sanitize, then send the result back postMessage(html_sanitize(event.data, urlTransformer, nameIdClassTransformer)); };
(Potrzeba trochę więcej kodu, aby biblioteka simworker działała, ale nie jest to ważne w tej dyskusji.)
Demo: https://dl.dropbox.com/u/291406/html-sanitize/demo.html
źródło
nameIdClassTransformer
jest wywoływana dla każdej nazwy HTML, identyfikatora elementu i listy klas; powrótnull
spowoduje usunięcie atrybutu. Edytując pliki JSON w src / com / google / caja / lang / html , możesz również dostosować, które elementy i atrybuty mają być umieszczone na białej liście.nameIdClassTranformer
powyższą funkcję, np. Aby odrzucić wszystkie<script>
tagi i zaakceptować<b>
i<i>
tagi?Nigdy nie ufaj klientowi. Jeśli piszesz aplikację serwerową, załóż, że klient zawsze będzie przesyłać niehigieniczne, złośliwe dane. Jest to praktyczna zasada, która uchroni Cię przed kłopotami. Jeśli możesz, radziłbym wykonać wszystkie sprawdzanie poprawności i czyszczenie w kodzie serwera, o którym wiesz (w rozsądnym stopniu) nie będzie się bawić. Być może mógłbyś użyć aplikacji internetowej na serwerze jako proxy dla kodu klienta, który pobiera od strony trzeciej i oczyszcza go przed wysłaniem go do samego klienta?
[edytuj] Przepraszam, źle zrozumiałem pytanie. Jednak trzymam się mojej rady. Twoi użytkownicy będą prawdopodobnie bezpieczniejsi, jeśli wyczyścisz serwer przed wysłaniem go do nich.
źródło
Teraz, gdy wszystkie główne przeglądarki obsługują elementy iframe w piaskownicy, myślę , że istnieje znacznie prostszy sposób, który może być bezpieczny. Chciałbym, żeby ta odpowiedź została przejrzana przez osoby lepiej zaznajomione z tego rodzaju kwestiami bezpieczeństwa.
UWAGA: Ta metoda na pewno nie będzie działać w IE 9 i wcześniejszych. Zobacz tę tabelę dla wersji przeglądarek obsługujących piaskownicę. (Uwaga: w tabeli wydaje się, że nie działa w Operze Mini, ale właśnie spróbowałem i zadziałało).
Chodzi o to, aby utworzyć ukryty element iframe z wyłączoną obsługą JavaScript, wkleić do niego niezaufany kod HTML i pozwolić mu go przeanalizować. Następnie możesz przejść po drzewie DOM i skopiować tagi i atrybuty, które są uważane za bezpieczne.
Białe listy pokazane tutaj to tylko przykłady. To, co najlepiej umieścić na białej liście, zależy od aplikacji. Jeśli potrzebujesz bardziej wyrafinowanych zasad niż tylko białe listy tagów i atrybutów, można je uwzględnić tą metodą, ale nie w tym przykładowym kodzie.
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); // necessary so the iframe contains a document 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; };
Możesz to wypróbować tutaj .
Zwróć uwagę, że w tym przykładzie nie zezwalam na atrybuty stylu i tagi. Gdybyś im na to pozwolił, prawdopodobnie chciałbyś przeanalizować CSS i upewnić się, że jest bezpieczny do twoich celów.
Przetestowałem to na kilku nowoczesnych przeglądarkach (Chrome 40, Firefox 36 Beta, IE 11, Chrome na Androida) i na jednej starej (IE 8), aby upewnić się, że wyskoczyło przed wykonaniem jakichkolwiek skryptów. Chciałbym wiedzieć, czy są jakieś przeglądarki, które mają z tym problem, lub jakieś skrajne przypadki, które przeoczę.
źródło
iframe.contentDocument.body.innerHTML = input
, wszelkie tagi skryptów zostaną wykonane.sandbox
atrybucie.Nie możesz przewidzieć każdego możliwego dziwnego rodzaju zniekształconych znaczników, o które jakaś przeglądarka może się potknąć, aby uniknąć czarnej listy, więc nie czarnej listy. Istnieje wiele innych struktur, które być może trzeba będzie usunąć, niż tylko skrypt / osadzenie / obiekt i programy obsługi.
Zamiast tego spróbuj przeanalizować kod HTML na elementy i atrybuty w hierarchii, a następnie uruchom wszystkie nazwy elementów i atrybutów na białej liście możliwie jak najmniejszej. Sprawdź również atrybuty adresu URL, które przepuszczasz, na białej liście (pamiętaj, że istnieją bardziej niebezpieczne protokoły niż tylko javascript :).
Jeśli dane wejściowe są dobrze sformułowane w XHTML, pierwsza część powyższego jest znacznie łatwiejsza.
Jak zawsze w przypadku sanityzacji HTML, jeśli możesz znaleźć inny sposób, aby tego uniknąć, zrób to. Potencjalnych dziur jest wiele, wiele. Jeśli po tylu latach główne usługi poczty internetowej nadal znajdują exploity, dlaczego myślisz, że możesz zrobić to lepiej?
źródło
npm
Mamy rok 2016 i myślę, że wielu z nas używa teraz modułów w naszym kodzie.sanitize-html
wydaje się być wiodącą opcją na npm, chociaż są inne .Inne odpowiedzi na to pytanie zapewniają duży wkład w tworzenie własnych, ale jest to na tyle trudny problem, że dobrze przetestowane rozwiązania społecznościowe są prawdopodobnie najlepszą odpowiedzią.
Uruchom to w wierszu poleceń, aby zainstalować:
npm install --save sanitize-html
ES5:
var sanitizeHtml = require('sanitize-html'); // ... var sanitized = sanitizeHtml(htmlInput);
ES6:
import sanitizeHtml from 'sanitize-html'; // ... let sanitized = sanitizeHtml(htmlInput);
źródło
[Zastrzeżenie: jestem jednym z autorów]
Napisaliśmy w tym celu bibliotekę open source „tylko internetową” (tj. „Wymaga przeglądarki”), https://github.com/jitbit/HtmlSanitizer, która usuwa wszystko
tags/attributes/styles
oprócz tych z „białej listy”.Stosowanie:
var input = HtmlSanitizer.SanitizeHtml("<script> Alert('xss!'); </scr"+"ipt>");
PS działa znacznie szybciej niż „czysty JavaScript”, ponieważ używa przeglądarki do analizowania i manipulowania DOM. Jeśli interesuje Cię „czysty JS”, wypróbuj https://github.com/punkave/sanitize-html (nie jest powiązany)
źródło
Zasugerowana powyżej biblioteka Google Caja była zbyt złożona, aby ją skonfigurować i uwzględnić w moim projekcie dla aplikacji internetowej (czyli działającej w przeglądarce). Zamiast tego uciekłem się do tego, ponieważ używamy już komponentu CKEditor, aby użyć jego wbudowanej funkcji dezynfekcji HTML i białej listy, która jest znacznie łatwiejsza do skonfigurowania. Możesz więc załadować instancję CKEditor do ukrytego elementu iframe i zrobić coś takiego:
CKEDITOR.instances['myCKEInstance'].dataProcessor.toHtml(myHTMLstring)
Oczywiście, jeśli nie używasz CKEditor w swoim projekcie, może to być trochę przesada, ponieważ sam komponent ma około pół megabajta (zminimalizowany), ale jeśli masz źródła, może możesz odizolować kod, wykonując białej listy (
CKEDITOR.htmlParser
?) i znacznie ją skrócić.http://docs.ckeditor.com/#!/api
http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor
źródło
Polecam wycinanie z życia frameworków, które na dłuższą metę znacznie ułatwiłyby życie.
cloneNode: Klonowanie kopie węzłów wszystkich jego atrybutów i ich wartości, ale nie NIE kopiować nasłuchiwania zdarzeń .
https://developer.mozilla.org/en/DOM/Node.cloneNode
Poniższe nie są testowane, chociaż używam treewalkerów od jakiegoś czasu i są one jedną z najbardziej niedocenianych części JavaScript. Oto lista typów węzłów, które możesz przeszukać, zwykle używam SHOW_ELEMENT lub SHOW_TEXT .
http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeFilter
function xhtml_cleaner(id) { var e = document.getElementById(id); var f = document.createDocumentFragment(); f.appendChild(e.cloneNode(true)); var walker = document.createTreeWalker(f,NodeFilter.SHOW_ELEMENT,null,false); while (walker.nextNode()) { var c = walker.currentNode; if (c.hasAttribute('contentEditable')) {c.removeAttribute('contentEditable');} if (c.hasAttribute('style')) {c.removeAttribute('style');} if (c.nodeName.toLowerCase()=='script') {element_del(c);} } alert(new XMLSerializer().serializeToString(f)); return f; } function element_del(element_id) { if (document.getElementById(element_id)) { document.getElementById(element_id).parentNode.removeChild(document.getElementById(element_id)); } else if (element_id) { element_id.parentNode.removeChild(element_id); } else { alert('Error: the object or element \'' + element_id + '\' was not found and therefore could not be deleted.'); } }
źródło