wake-up-neo.com

Kann ich einen Browser bitten, <script> s nicht in einem Element auszuführen?

Ist es möglich, Browser anzuweisen, JavaScript nicht in bestimmten Teilen eines HTML-Dokuments auszuführen?

Mögen:

<div script="false"> ...

Dies könnte als zusätzliches Sicherheitsmerkmal nützlich sein. Alle gewünschten Skripte werden in einen bestimmten Teil des Dokuments geladen. Es sollten keine Skripte in anderen Teilen des Dokuments vorhanden sein, und falls vorhanden, sollten sie nicht ausgeführt werden.

81
Seppo Erviälä

JA, Sie können :-) Die Antwort lautet: Content Security Policy (CSP).

Die meisten modernen Browser unterstützen dieses Flag , wodurch der Browser angewiesen wird, nur JavaScript-Code aus einer vertrauenswürdigen externen Datei zu laden und den gesamten internen JavaScript-Code nicht zuzulassen! Der einzige Nachteil ist, dass Sie kein Inline-JavaScript auf Ihrer gesamten Seite verwenden können (nicht nur für ein einzelnes <div>). Es könnte zwar eine Problemumgehung geben, wenn Sie das div dynamisch aus einer externen Datei mit einer anderen Sicherheitsrichtlinie einbinden, aber ich bin mir nicht sicher.

Wenn Sie jedoch Ihre Website so ändern können, dass alle JavaScript-Dateien aus externen JavaScript-Dateien geladen werden, können Sie Inline-JavaScript mit dieser Kopfzeile insgesamt deaktivieren!

Hier ist ein nettes Tutorial mit Beispiel: HTML5Rocks Tutorial

Wenn Sie den Server so konfigurieren können, dass er dieses HTTP-Header-Flag sendet, ist die Welt ein besserer Ort!

90
Falco

Sie können JavaScript blockieren, das von <script> Geladen wird, indem Sie beforescriptexecute event verwenden:

<script>
  // Run this as early as possible, it isn't retroactive
  window.addEventListener('beforescriptexecute', function(e) {
    var el = e.target;
    while(el = el.parentElement)
      if(el.hasAttribute('data-no-js'))
        return e.preventDefault(); // Block script
  }, true);
</script>

<script>console.log('Allowed. Console is expected to show this');</script>
<div data-no-js>
  <script>console.log('Blocked. Console is expected to NOT show this');</script>
</div>

Beachten Sie, dass beforescriptexecute in HTML 5.0 definiert wurde, aber in HTML 5.1 entfernt wurde. Firefox ist der einzige große Browser, der es implementiert hat.

Wenn Sie eine nicht vertrauenswürdige Menge von HTML in Ihre Seite einfügen, sollten Sie beachten, dass das Blockieren von Skripten in diesem Element keine höhere Sicherheit bietet, da das nicht vertrauenswürdige HTML das Sandkastenelement schließen kann und das Skript daher außerhalb platziert und ausgeführt wird.

Und das wird Dinge wie <img onerror="javascript:alert('foo')" src="//" /> nicht blockieren.

13
Oriol

Interessante Frage, ich denke nicht, dass es möglich ist. Aber selbst wenn es so ist, hört es sich so an, als wäre es ein Hack.

Wenn der Inhalt dieses div nicht vertrauenswürdig ist, müssen Sie die Daten auf der Serverseite maskieren, bevor sie in der HTTP-Antwort gesendet und im Browser gerendert werden.

Wenn Sie nur <script> - Tags entfernen und andere HTML-Tags zulassen möchten, entfernen Sie sie einfach aus dem Inhalt und lassen Sie den Rest übrig.

Sehen Sie sich die XSS-Prävention an.

https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet

8
cowls

JavaScript wird "inline" ausgeführt, dh in der Reihenfolge, in der es im DOM angezeigt wird (wenn dies nicht der Fall wäre, könnten Sie nie sicher sein, dass eine in einem anderen Skript definierte Variable bei der ersten Verwendung sichtbar war ).

Theoretisch bedeutet dies, dass Sie am Anfang der Seite ein Skript haben könnten (d. H. Das erste <script> -Element), das das DOM durchsucht und alle <script> -Elemente und Ereignishandler in Ihrem <div> entfernt.

Die Realität ist jedoch komplexer: Das Laden von DOM und Skripten erfolgt asynchron. Dies bedeutet, dass der Browser nur garantiert, dass ein Skript den Teil des DOM sehen kann, der vor ist (d. H. Die Überschrift in unserem Beispiel). Es gibt keine Garantien für etwas darüber hinaus (dies bezieht sich auf document.write()). Vielleicht sehen Sie das nächste Skript-Tag oder nicht.

Sie könnten sich an das Ereignis onload des Dokuments halten - wodurch sichergestellt wird, dass Sie das gesamte DOM erhalten -, aber zu diesem Zeitpunkt könnte bösartiger Code bereits ausgeführt worden sein. Es wird schlimmer, wenn andere Skripte das DOM manipulieren und dort Skripte hinzufügen. Sie müssten also auch nach jeder Änderung des DOM suchen.

Die @cowls-Lösung (Filtern auf dem Server) ist also die einzige Lösung, die in allen Situationen zum Funktionieren gebracht werden kann.

7
Aaron Digulla

Ich habe eine Theorie:

  • Wickeln Sie den spezifischen Teil des Dokuments in ein noscript -Tag.
  • Verwenden Sie DOM-Funktionen, um alle script -Tags innerhalb des noscript -Tags zu verwerfen und den Inhalt zu entpacken.

Beispiel für einen Proof of Concept:

window.onload = function() {
    var noscripts = /* _live_ list */ document.getElementsByTagName("noscript"),
        memorydiv = document.createElement("div"),
        scripts = /* _live_ list */ memorydiv.getElementsByTagName("script"),
        i,
        j;
    for (i = noscripts.length - 1; i >= 0; --i) {
        memorydiv.innerHTML = noscripts[i].textContent || noscripts[i].innerText;
        for (j = scripts.length - 1; j >= 0; --j) {
            memorydiv.removeChild(scripts[j]);
        }
        while (memorydiv.firstChild) {
            noscripts[i].parentNode.insertBefore(memorydiv.firstChild, noscripts[i]);
        }
        noscripts[i].parentNode.removeChild(noscripts[i]);
    }
};
body { font: medium/1.5 monospace; }
p, h1 { margin: 0; }
<h1>Sample Content</h1>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
<h1>Sample Content in No-JavaScript Zone</h1>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>
1
Salman A

Wenn Sie JavaScript-Code in Ihrem Browser anzeigen möchten:

Mit JavaScript und HTML müssen Sie HTML-Entities verwenden, um den JavaScript-Code anzuzeigen und zu vermeiden, dass dieser Code ausgeführt wird. Hier finden Sie die Liste der HTML-Entitäten:

Wenn Sie eine serverseitige Skriptsprache (PHP, ASP.NET usw.) verwenden, gibt es höchstwahrscheinlich eine Funktion, die einen String maskiert und die Sonderzeichen in HTML-Entitäten konvertiert. In PHP würden Sie "htmlspecialchars ()" oder "htmlentities ()" verwenden. Letzteres deckt alle HTML-Zeichen ab.

Wenn Sie Ihren JavaScript-Code auf eine nette Art und Weise anzeigen möchten, probieren Sie einen der folgenden Code-Hervorhebungen:

1
Wissam El-Kik

Wenn Sie Skript-Tags später wieder aktivieren möchten, bestand meine Lösung darin, die Browser-Umgebung zu unterbrechen, sodass jedes ausgeführte Skript einen Fehler relativ früh auslöst. Es ist jedoch nicht absolut zuverlässig, sodass Sie es nicht als Sicherheitsmerkmal verwenden können.

Wenn Sie versuchen, auf globale Eigenschaften zuzugreifen, löst Chrome eine Ausnahme aus.

setTimeout("Math.random()")
// => VM116:25 Uncaught Error: JavaScript Execution Inhibited  

Ich überschreibe alle überschreibbaren Eigenschaften von window, aber Sie können sie auch erweitern, um andere Funktionen zu beeinträchtigen.

window.allowJSExecution = inhibitJavaScriptExecution();
function inhibitJavaScriptExecution(){

    var windowProperties = {};
    var Object = window.Object
    var console = window.console
    var Error = window.Error

    function getPropertyDescriptor(object, propertyName){
        var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
        if (!descriptor) {
            return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
        }
        return descriptor;
    }

    for (var propName in window){
        try {
            windowProperties[propName] = getPropertyDescriptor(window, propName)
            Object.defineProperty(window, propName, {
                get: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                set: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                configurable: true
            })
        } catch (err) {}
    }

    return function allowJSExecution(){
        for (var propName in window){
            if (!(propName in windowProperties)) {
                delete windowProperties[propName]
            }
        }

        for (var propName in windowProperties){
            try {
                Object.defineProperty(window, propName, windowProperties[propName])
            } catch (err) {}
        }
    }
}
0
Matt Zeunert