webentwicklung-frage-antwort-db.com.de

HTML auf der Clientseite sanieren/umschreiben

Ich muss externe Ressourcen anzeigen, die über domänenübergreifende Anforderungen geladen wurden, und nur "safe" - Inhalte anzeigen. 

Könnte Prototypes String # stripScripts verwenden, um Skriptblöcke zu entfernen. Handler wie onclick oder onerror sind jedoch immer noch vorhanden.

Gibt es eine Bibliothek, die das zumindest kann?

  • streifenblockblöcke,
  • töte DOM-Handler,
  • entfernen Sie schwarz aufgelistete Tags (zB: embed oder object).

Gibt es irgendwelche JavaScript-bezogenen Links und Beispiele?

67
aemkei

Update 2016: Es gibt jetzt ein Google Closure - Paket, das auf dem Caja-Desinfektionsmittel basiert.

Es verfügt über eine sauberere API, wurde neu geschrieben, um die in modernen Browsern verfügbaren APIs zu berücksichtigen, und arbeitet besser mit Closure Compiler zusammen.


Shameless plug: Siehe caja/plugin/html-sanitizer.js für einen html-sanitizer auf Client-Seite, der gründlich überprüft wurde.

Es ist eine weiße Liste, keine schwarzen Liste, aber die Whitelists sind gemäß CajaWhitelists konfigurierbar.


Wenn Sie alle Tags entfernen möchten, gehen Sie wie folgt vor:

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, '&lt;');
}

Die Leute werden Ihnen sagen, dass Sie ein Element erstellen können, und Sie können innerHTML zuweisen und dann die innerText oder textContent erhalten und Entitäten darin entgehen. TU das nicht. Es ist anfällig für XSS-Injection, da <img src=bogus onerror=alert(1337)> den onerror-Handler ausführt, auch wenn der Knoten niemals mit dem DOM verbunden ist.

98
Mike Samuel

Der Google Caja HTML-Sanitizer kann "web-ready" gemacht werden, indem er in einen web worker eingebettet wird. Alle vom Sanitizer eingeführten globalen Variablen sind im Worker enthalten, und die Verarbeitung erfolgt in einem eigenen Thread.

Für Browser, die Web Workers nicht unterstützen, können wir einen iframe als separate Umgebung verwenden, in der der Desinfizierer arbeiten kann. Timothy Chien hat eine polyfill , die genau dies tut, indem sie iframes verwendet, um Web Workers zu simulieren für uns erledigt.

Das Caja-Projekt hat eine Wiki-Seite zu wie man Caja als eigenständiges clientseitiges Desinfektionsmittel verwendet :

  • Überprüfen Sie die Quelle und erstellen Sie anschließend ant.
  • Fügen Sie html-sanitizer-minified.js oder html-css-sanitizer-minified.js in Ihre Seite ein
  • Aufruf html_sanitize(...)

Das Arbeiterskript muss nur diesen Anweisungen folgen:

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));
};

(Ein wenig mehr Code ist erforderlich, um die Simworker-Bibliothek zum Laufen zu bringen, aber für diese Diskussion ist es nicht wichtig.)

Demo: https://dl.dropbox.com/u/291406/html-sanitize/demo.html

38
Jeffery To

Vertrauen Sie niemals dem Kunden. Wenn Sie eine Serveranwendung schreiben, gehen Sie davon aus, dass der Client immer unhygienische, bösartige Daten übermittelt. Es ist eine Faustregel, die Sie aus Schwierigkeiten heraushalten wird. Wenn Sie können, würde ich Ihnen raten, alle Validierungs- und Sanierungsmaßnahmen in Server-Code durchzuführen, die Sie (in einem vernünftigen Maße) nicht kennen. Vielleicht können Sie eine serverseitige Webanwendung als Proxy für Ihren clientseitigen Code verwenden, der vom Drittanbieter abgerufen wird und die Hygiene vornimmt, bevor er an den Client selbst gesendet wird.

Es tut mir leid, ich habe die Frage falsch verstanden. Ich stehe jedoch zu meinem Rat. Ihre Benutzer sind wahrscheinlich sicherer, wenn Sie den Server vor dem Senden an den Server bereinigen.

14
Sean Edwards

Sie können nicht jeden möglichen merkwürdigen Typ von fehlerhaftem Markup vorhersehen, den ein Browser irgendwohin stolpern könnte, um der Blacklist zu entgehen, also keine Blacklist. Es gibt viele mehr Strukturen, die Sie möglicherweise entfernen müssen, als nur Skript/Einbettung/Objekt und Handler.

Versuchen Sie stattdessen, den HTML-Code in Elemente und Attribute in einer Hierarchie zu parsen, und führen Sie dann alle Element- und Attributnamen anhand einer möglichst geringen Whitelist aus. Prüfen Sie auch alle URL-Attribute, die Sie durchlassen, mit einer Whitelist (denken Sie daran, dass es gefährlichere Protokolle gibt als nur Javascript :).

Wenn die Eingabe wohlgeformtes XHTML ist, ist der erste Teil der obigen Ausführungen viel einfacher.

Wenn Sie einen anderen Weg finden, dies zu vermeiden, tun Sie dies wie immer bei der HTML-Bereinigung. Es gibt viele, viele potenzielle Löcher. Wenn die großen Webmail-Dienste nach so vielen Jahren immer noch Exploits finden, was glauben Sie, können Sie besser machen?

12
bobince

Jetzt, da alle gängigen Browser Sandbox-Iframes unterstützen, gibt es einen viel einfacheren Weg, den ich für sicher halte . Es würde mich freuen, wenn diese Antwort von Personen überprüft werden könnte, die mit dieser Art von Sicherheitsproblem besser vertraut sind.

HINWEIS: Diese Methode funktioniert definitiv nicht in IE 9 und früher. Siehe diese Tabelle für Browserversionen, die Sandboxing unterstützen. (Hinweis: Die Tabelle scheint zu sagen, dass es nicht funktioniert in Opera Mini, aber ich habe es nur ausprobiert, und es hat funktioniert.)

Die Idee ist, einen versteckten Iframe mit deaktiviertem JavaScript zu erstellen, Ihren nicht vertrauenswürdigen HTML-Code darin einzufügen und ihn analysieren zu lassen. Anschließend können Sie den DOM-Baum durchgehen und die als sicher geltenden Tags und Attribute kopieren.

Die hier gezeigten Whitelists sind nur Beispiele. Was für eine Whitelist am besten geeignet ist, hängt von der Anwendung ab. Wenn Sie eine anspruchsvollere Richtlinie als nur Whitelists mit Tags und Attributen benötigen, kann dies mit dieser Methode berücksichtigt werden, jedoch nicht mit diesem Beispielcode.

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;
};

Sie können es ausprobieren hier .

Beachten Sie, dass ich in diesem Beispiel Stilattribute und Tags nicht zulasse. Wenn Sie dies zulassen, möchten Sie wahrscheinlich das CSS analysieren und sicherstellen, dass es für Ihre Zwecke sicher ist.

Ich habe dies auf mehreren modernen Browsern (Chrome 40, Firefox 36 Beta, IE 11, Chrome für Android)) und auf einem alten (IE) getestet 8) um ​​sicherzustellen, dass es nicht mehr funktioniert, bevor Skripte ausgeführt werden. Es würde mich interessieren, ob es Browser gibt, die Probleme damit haben, oder Edge-Fälle, die ich übersehen habe.

11
aldel

Es ist 2016, und ich denke, viele von uns verwenden jetzt npm-Module in unserem Code. sanitize-html scheint die führende Option auf npm zu sein, obwohl es andere gibt.

Andere Antworten auf diese Frage liefern einen großartigen Beitrag zum Würfeln, aber dies ist ein kniffliges Problem, bei dem bewährte Community-Lösungen wahrscheinlich die beste Antwort sind.

Führen Sie dies in der Befehlszeile aus, um Folgendes zu installieren: 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);

7
ericsoco
String.prototype.sanitizeHTML=function (white,black) {
   if (!white) white="b|i|p|br";//allowed tags
   if (!black) black="script|object|embed";//complete remove tags
   var e=new RegExp("(<("+black+")[^>]*>.*</\\2>|(?!<[/]?("+white+")(\\s[^<]*>|[/]>|>))<[^<>]*>|(?!<[^<>\\s]+)\\s[^</>]+(?=[/>]))", "gi");
   return this.replace(e,"");
}

-black list -> Tag und Inhalt vollständig entfernen

-weiße Liste -> Tags beibehalten 

Andere Tags werden entfernt, der Inhalt des Tags bleibt jedoch erhalten

-alle Attribute der White-List-Tags (die übrigen) werden entfernt

5
neu-rah

Die oben vorgeschlagene Google Caja-Bibliothek war viel zu komplex, um sie für eine Webanwendung zu konfigurieren und in mein Projekt aufzunehmen (also im Browser auszuführen). Stattdessen habe ich, da wir bereits die CKEditor-Komponente verwenden, auf die integrierte HTML-Bereinigungs- und Whitelisting-Funktion zurückgegriffen, die viel einfacher zu konfigurieren ist. Sie können also eine CKEditor-Instanz in einen verborgenen Iframe laden und Folgendes tun:

CKEDITOR.instances['myCKEInstance'].dataProcessor.toHtml(myHTMLstring)

Wenn Sie CKEditor nicht in Ihrem Projekt verwenden, ist dies möglicherweise ein wenig übertrieben, da die Komponente selbst etwa ein halbes Megabyte groß ist (minimiert). Wenn Sie jedoch über die Quellen verfügen, können Sie den Code isolieren das Whitelisting (CKEDITOR.htmlParser?) und kürzer machen.

http://docs.ckeditor.com/#!/api

http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor

1
AsGoodAsItGets

[Haftungsausschluss: Ich bin einer der Autoren]

Wir haben eine Open-Source-Bibliothek mit dem Namen "Nur Web" (d. H. "Erfordert einen Browser") geschrieben: https://github.com/jitbit/HtmlSanitizer , die alle tags/attributes/styles-Dateien mit Ausnahme der "Whitelist" entfernt.

Verwendungszweck:

var input = HtmlSanitizer.SanitizeHtml("<script> Alert('xss!'); </scr"+"ipt>");

P.S. Funktioniert viel schneller als eine "reine JavaScript" -Lösung, da der Browser zum Analysieren und Bearbeiten von DOM verwendet wird. Wenn Sie sich für eine "pure JS" -Lösung interessieren, versuchen Sie bitte https://github.com/punkave/sanitize-html (nicht angeschlossen)

1
Alex

Ich empfehle, Rahmen aus Ihrem Leben zu schneiden, dies würde Ihnen die Dinge auf lange Sicht übermäßig erleichtern.

cloneNode: Beim Klonen eines Knotens werden alle Attribute und deren Werte kopiert, do NOT copy event listeners.

https://developer.mozilla.org/de/DOM/Node.cloneNode

Folgendes wurde nicht getestet, obwohl ich Treewalker bereits seit einiger Zeit verwende und sie sind einer der am meisten unterbewerteten Teile von JavaScript. Hier ist eine Liste der Knotentypen, die Sie durchsuchen können. Normalerweise verwende ich SHOW_ELEMENT oder 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.');
 }
}
0
John