it-swarm.com.de

Ist es möglich, JavaScript im Browser auszuführen?

Ich frage mich, ob es möglich ist, JavaScript im Browser auszuführen, um den Zugriff auf Funktionen zu verhindern, die normalerweise für JavaScript-Code verfügbar sind, der auf einer HTML-Seite ausgeführt wird.

Angenommen, ich möchte eine JavaScript-API für Endbenutzer bereitstellen, damit diese Ereignisbehandlungsroutinen definieren können, die bei "interessanten Ereignissen" ausgeführt werden. Ich möchte jedoch nicht, dass diese Benutzer auf die Eigenschaften und Funktionen des window-Objekts zugreifen. Kann ich das machen? 

Nehmen wir im einfachsten Fall an, ich möchte verhindern, dass Benutzer alert aufrufen. Ein paar Ansätze, die mir einfallen, sind:

  • window.alert global neu definieren. Ich glaube nicht, dass dies ein gültiger Ansatz wäre, da anderer Code, der auf der Seite ausgeführt wird (d. H. Nicht von Benutzern in ihren Ereignisprozeduren verfasste Elemente), alert verwenden möchte.
  • Senden Sie den Ereignishandlercode an den Server, der verarbeitet werden soll. Ich bin nicht sicher, ob das Senden des Codes an den Server zur Verarbeitung der richtige Ansatz ist, da die Ereignishandler im Kontext der Seite ausgeführt werden müssen. 

Vielleicht eine Lösung, bei der der Server die benutzerdefinierte Funktion verarbeitet und dann einen Rückruf generiert, der auf dem Client ausgeführt werden soll? Auch wenn dieser Ansatz funktioniert, gibt es bessere Wege, um dieses Problem zu lösen?

128
Walter Rumsby

Google Caja ist ein Source-to-Source-Übersetzer, mit dem Sie "nicht vertrauenswürdigen HTML-Code und JavaScript von Drittanbietern in Ihre Seite integrieren können und trotzdem sicher sein können."

54
Darius Bacon

Schauen Sie sich Douglas Crockfords ADsafe an:

ADsafe macht es sicher, auf jeder Webseite Gastcode (wie von Drittanbietern erstellte Werbung oder Widgets) zu platzieren. ADsafe definiert eine Teilmenge von JavaScript, die stark genug ist, um es dem Gastcode zu ermöglichen, wertvolle Interaktionen durchzuführen und gleichzeitig böswillige oder versehentliche Schäden oder Eindringlinge zu verhindern. Das ADsafe-Subset kann mechanisch mit Tools wie JSLint überprüft werden, sodass keine Überprüfung durch den Benutzer erforderlich ist, um den Code des Gastes auf Sicherheit zu überprüfen. Das ADsafe-Subset erzwingt außerdem gute Codierungspraktiken, wodurch die Wahrscheinlichkeit steigt, dass der Gastcode ordnungsgemäß ausgeführt wird.

Ein Beispiel für die Verwendung von ADsafe finden Sie in den Dateien template.html und template.js in im GitHub-Repository des Projekts .

32
Simon Lieschke

Ich habe eine Sandbox-Bibliothek mit dem Namen jsandbox erstellt, die Web-Worker zum Auswerten von Sandbox-Code verwendet. Es hat auch eine Eingabemethode, um explizit Sandkastencode-Daten bereitzustellen, die sonst nicht möglich wären.

Das folgende Beispiel zeigt die API:

jsandbox
    .eval({
      code    : "x=1;Math.round(Math.pow(input, ++x))",
      input   : 36.565010597564445,
      callback: function(n) {
          console.log("number: ", n); // number: 1337
      }
  }).eval({
      code   : "][];.]\\ (*# ($(! ~",
      onerror: function(ex) {
          console.log("syntax error: ", ex); // syntax error: [error object]
      }
  }).eval({
      code    : '"foo"+input',
      input   : "bar",
      callback: function(str) {
          console.log("string: ", str); // string: foobar
      }
  }).eval({
      code    : "({q:1, w:2})",
      callback: function(obj) {
          console.log("object: ", obj); // object: object q=1 w=2
      }
  }).eval({
      code    : "[1, 2, 3].concat(input)",
      input   : [4, 5, 6],
      callback: function(arr) {
          console.log("array: ", arr); // array: [1, 2, 3, 4, 5, 6]
      }
  }).eval({
      code    : "function x(z){this.y=z;};new x(input)",
      input   : 4,
      callback: function(x) {
          console.log("new x: ", x); // new x: object y=4
      }
  });
23
Eli Grey

EDIT: Obwohl ich keinen Ausweg aus der Whitelist unten weiß, würde ich den Arbeiter auch aus einem Sandkasten <iframe> laufen lassen, nur für den Fall, und würde @ gronostajs js.js Empfehlung bei Erschöpfung des Speichers ablehnen ist ein Anliegen.

Webworker bieten eine bequeme Möglichkeit, einen anderen Skriptkontext zu erstellen, der aggressiv in einen Sandkasten umgewandelt werden kann, ohne dass der übergeordnete Benutzer davon betroffen ist.

Eine Implementierung mit Versprechen:

function safeEval(untrustedCode) {
    return new Promise(function (resolve, reject) {
        var worker = new Worker('eval.js');

        worker.onmessage = function (e) {
            worker.terminate();
            resolve(e.data);
        };

        worker.onerror = function (e) {
            reject(new Error(e.message));
        };

        worker.postMessage(untrustedCode);

        setTimeout(function () {
            worker.terminate();
            reject(new Error('The worker timed out.'));
        }, 1000);
    });
}

eval.js (Sie möchten wahrscheinlich die Whitelist erweitern):

(function (global) {
    'use strict';

    var _postMessage = postMessage;
    var _addEventListener = addEventListener;

    (function () {
        var current = global;
        var keepProperties = [
            // required
            'Object', 'Function', 'Infinity', 'NaN', 'undefined',
            // optional, but trivial to get back
            'Array', 'Boolean', 'Number', 'String', 'Symbol',
            // optional
            'Map', 'Math', 'Set',
        ];

        do {
            Object.getOwnPropertyNames(current).forEach(function (name) {
                if (keepProperties.indexOf(name) === -1) {
                    delete current[name];
                }
            });

            current = Object.getPrototypeOf(current);
        } while (current !== Object.prototype);
    })();

    _addEventListener('message', function (e) {
        var f = new Function('', 'return (' + e.data + '\n);');
        _postMessage(f());
    });
})(this);

Kein Zugriff auf die Benutzeroberfläche von Workern, kann separat beendet werden, kann die Benutzeroberfläche oder das ausgeführte Skript nicht binden und ist Standard.

20
Ry-

Ich denke, dass js.js hier erwähnenswert ist. Es ist ein JavaScript-Interpreter, der in JavaScript geschrieben ist.

Es ist etwa 200 Mal langsamer als native JS, aber aufgrund seiner Beschaffenheit ist es eine perfekte Sandbox-Umgebung. Ein weiterer Nachteil ist die Größe von fast 600 KB, was in manchen Fällen für Desktops akzeptabel sein kann, für mobile Geräte jedoch nicht.

7
gronostaj

Wie bereits in anderen Erwiderungen erwähnt, reicht es aus, den Code in einem Sandkasten-Iframe zu sperren (ohne ihn an den Server zu senden) und mit Nachrichten zu kommunizieren. Ich würde vorschlagen, einen Blick auf eine kleine Bibliothek zu werfen, die ich vor allem erstellt habe, um API für den nicht vertrauenswürdigen Code bereitzustellen, genau wie in der Frage beschrieben: Es gibt die Möglichkeit, bestimmte Funktionen richtig zu exportieren in die Sandbox, in der der nicht vertrauenswürdige Code ausgeführt wird. Und es gibt auch eine Demo, die den von einem Benutzer in einer Sandbox übermittelten Code ausführt:

http://asvd.github.io/jailed/demos/web/console/

6
asvd

Alle Browseranbieter und die HTML5-Spezifikation arbeiten auf eine tatsächliche Sandbox-Eigenschaft hin, um Sandkasten-Iframes zuzulassen. Dies ist jedoch immer noch auf die Iframe-Granularität beschränkt.

Im Allgemeinen kann kein beliebiges Maß an regulären Ausdrücken usw. beliebig vom Benutzer bereitgestelltes JavaScript sicher bereinigen, da es zum Halteproblem führt: - /

4
olliej

Ein hässlicher Weg, aber vielleicht funktioniert das für Sie. Ich habe alle Globals im Sandbox-Bereich neu definiert und den strikten Modus hinzugefügt, sodass sie das globale Objekt nicht mit einer anonymen Funktion erhalten können.

function construct(constructor, args) {
  function F() {
      return constructor.apply(this, args);
  }
  F.prototype = constructor.prototype;
  return new F();
}
// Sanboxer 
function sandboxcode(string, inject) {
  "use strict";
  var globals = [];
  for (var i in window) {
    // <--REMOVE THIS CONDITION
    if (i != "console")
    // REMOVE THIS CONDITION -->
    globals.Push(i);
  }
  globals.Push('"use strict";\n'+string);
  return construct(Function, globals).apply(inject ? inject : {});
}
sandboxcode('console.log( this, window, top , self, parent, this["jQuery"], (function(){return this;}()));'); 
// => Object {} undefined undefined undefined undefined undefined undefined 
console.log("return of this", sandboxcode('return this;', {window:"sanboxed code"})); 
// => Object {window: "sanboxed code"}

https://Gist.github.com/alejandrolechuga/9381781

3
alejandro

Eine verbesserte Version von @ RyanOHaras Web-Worker-Sandbox-Code in einer single-Datei (keine zusätzliche eval.js-Datei ist erforderlich).

function safeEval(untrustedCode)
    {
    return new Promise(function (resolve, reject)
    {

    var blobURL = URL.createObjectURL(new Blob([
        "(",
        function ()
            {
            var _postMessage = postMessage;
            var _addEventListener = addEventListener;

            (function (obj)
                {
                "use strict";

                var current = obj;
                var keepProperties = [
                    // required
                    'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT', 
                    // optional, but trivial to get back
                    'Array', 'Boolean', 'Number', 'String', 'Symbol',
                    // optional
                    'Map', 'Math', 'Set',
                ];

                do {
                    Object.getOwnPropertyNames(current).forEach(function (name) {
                        if (keepProperties.indexOf(name) === -1) {
                            delete current[name];
                        }
                    });

                    current = Object.getPrototypeOf(current);
                }
                while (current !== Object.prototype);
                })(this);

            _addEventListener("message", function (e)
            {
            var f = new Function("", "return (" + e.data + "\n);");
            _postMessage(f());
            });
            }.toString(),
        ")()"], {type: "application/javascript"}));

    var worker = new Worker(blobURL);

    URL.revokeObjectURL(blobURL);

    worker.onmessage = function (evt)
        {
        worker.terminate();
        resolve(evt.data);
        };

    worker.onerror = function (evt)
        {
        reject(new Error(evt.message));
        };

    worker.postMessage(untrustedCode);

    setTimeout(function () {
        worker.terminate();
        reject(new Error('The worker timed out.'));
        }, 1000);
    });
    }

Probier es aus:

https://jsfiddle.net/kp0cq6yw/

var promise = safeEval("1+2+3");

promise.then(function (result) {
      alert(result);
      });

Es sollte 6 (getestet in Chrome und Firefox) ausgegeben werden.

2
MarcG

Ab 2019 scheint vm2 die beliebteste und am häufigsten aktualisierte Lösung für dieses Problem zu sein.

0
Bret Cameron

Ein unabhängiger Javascript-Interpreter liefert wahrscheinlich eher eine robuste Sandbox als eine in Käfigen gehaltene Version der integrierten Browserimplementierung. Ryan hat bereits erwähntjs.js , aber ein aktuelleres Projekt ist JS-Interpreter . In docs wird beschrieben, wie verschiedene Funktionen für den Interpreter verfügbar gemacht werden können, der Umfang ist jedoch ansonsten sehr begrenzt. 

0
David Fraser