Zum Hauptinhalt springen
Javascript

Schreiben eines JavaScript-Frameworks - Sandbox-Code-Auswertung




En este capítulo, explicaré las diferentes formas de evaluar el código en el Browser y los problemas que causan. También introduciré un método, que se basa en algunas características nuevas o menos conocidas de JavaScript.

Die missbrauchte Bewertung

Die Funktion eval () wertet den als Zeichenfolge dargestellten JavaScript-Code aus.

Eine übliche Lösung für die Code-Auswertung ist die Funktion eval (). Der Code, der mit ausgewertet wird eval () tiene acceso a cierres y al Umfang global, lo que conduce a un problema de seguridad denominado inyección de código; y hace que eval () Sein una de las características más notorias de JavaScript.

Trotz verpönt, eval () es ist in einigen Situationen sehr nützlich. Die meisten Frames Vorderes Ende Moderne erfordern ihre Funktionalität, trauen sich jedoch aufgrund des oben genannten Problems nicht, sie zu verwenden. Infolgedessen ergaben sich viele Problemumgehungen für die Bewertung von Zeichenfolgen in a Sandkasten eher als globale Reichweite. Das Sandkasten verhindert, dass Code auf sichere Daten zugreift. Normalerweise handelt es sich um ein einfaches JavaScript-Objekt, das das globale Objekt für den ausgewerteten Code überschreibt.

Der übliche Weg

Die Alternative zu eval () más común es la reimplementación completa: un proceso de dos pasos, que consiste en analizar e interpretar la cadena pasada. Primero, el analizador crea un árbol de sintaxis abstracta, luego el intérprete recorre el árbol y lo interpreta como un código dentro de una sandbox (caja de arena).

Dies ist eine weit verbreitete Lösung, aber wohl zu schwer für etwas so Einfaches. Schreiben Sie alles von Grund auf neu, anstatt Patches anzuwenden eval () bietet viele Möglichkeiten für Fehler und erfordert häufige Änderungen, um auch den neuesten Sprachaktualisierungen zu folgen.

Ein alternativer Weg

NX versucht, die erneute Bereitstellung von nativem Code zu vermeiden. Die Auswertung wird von einer kleinen Bibliothek durchgeführt, die einige neue oder weniger bekannte JavaScript-Funktionen verwendet.

In diesem Abschnitt werden diese Funktionen schrittweise vorgestellt und zur Erläuterung der Code-Evaluierungsbibliothek verwendet. nx-kompilieren. Die Bibliothek hat eine Funktion namens compileCode (), das funktioniert wie folgt:

const code = compileCode ('return num1 + num2') // dies protokolliert 17 in console console.log (code ({num1: 10, num2: 7})) const globalNum = 12 const otherCode = compileCode ('return globalNum') // globaler Bereichszugriff wird verhindert, dieser Datensatz ist nicht in der Konsole console.log definiert (otherCode ({num1: 2, num2: 3}))

Am Ende dieses Artikels werden wir die Funktion implementieren compileCode () in weniger als 20 Zeilen.

neue Funktion ()

Der Bauarbeiter Funktion erstelle eine neue Objekt Funktion. In JavaScript ist jede Funktion tatsächlich ein Funktionsobjekt.

Das Funktionskonstruktor ist eine Alternative zu eval (). neue Funktion (… args, 'funcBody') Bewerten Sie die Zeichenfolge 'funcBody' als Code übergeben und gibt eine neue Funktion zurück, die diesen Code ausführt. Es unterscheidet sich von eval () in zweierlei Hinsicht.

  • Übergeben Sie den übergebenen Code nur einmal. Durch Aufrufen der zurückgegebenen Funktion wird der Code ausgeführt, ohne ihn erneut auszuwerten.
  • Sie haben keinen Zugriff auf die lokalen Abschlussvariablen, können jedoch weiterhin auf den globalen Bereich zugreifen.
Funktion compileCode (src) {neue Funktion (src) zurückgeben}

neue Funktion (), ist eine bessere Alternative für eval () in unserem Anwendungsfall. Es bietet überlegene Leistung und Sicherheit, aber der Zugriff auf die globale Reichweite muss immer noch vermieden werden, um rentabel zu sein.

Die Stichwort ‘with’

Die Anleitung mit erweitert die Bereichszeichenfolge einer Anweisung.

mit ist ein weniger bekanntes Schlüsselwort in JavaScript. Es ermöglicht eine halb sandgestrahlte Ausführung. Der Code innerhalb eines Blocks mit Es wird versucht, die Variablen aus dem zuerst übergebenen Sandbox-Objekt abzurufen. Wenn es dort jedoch nicht gefunden werden kann, wird nach der Variablen im Abschluss und im globalen Bereich gesucht. Der Zugriff auf den Schließbereich wird durch verhindert neue Funktion () worüber wir uns nur im globalen Bereich Sorgen machen müssen.

Funktion compileCode (src) {src = 'with (sandbox) {' + src + '}' neue Funktion zurückgeben ('sandbox', src)}

mit, verwendet den Inoperator intern. Bewerten Sie für jeden Variablenzugriff innerhalb des Blocks die Variable in der Bedingung Sandkasten. Wenn die Bedingung erfüllt ist, rufen Sie die Variable aus der Sandbox ab. Andernfalls wird nach der Variablen im globalen Bereich gesucht. Wenn wir verwenden mit um die Variable in der Sandbox immer als auszuwerten wahr, Wir könnten verhindern, dass es auf den globalen Bereich zugreift.

ES6-Proxys

Das Objekt Proxy Es wird verwendet, um benutzerdefiniertes Verhalten für grundlegende Vorgänge wie Suchen oder Zuordnen von Eigenschaften zu definieren.

EIN ES6-Proxy Wraps ein Objekt und definiert Catch-Funktionen, die grundlegende Operationen an diesem Objekt abfangen können. Erfassungsfunktionen werden aufgerufen, wenn eine Operation ausgeführt wird. Durch Einwickeln des Testbereichsobjekts in eine Falle Proxy Wir können das Standardverhalten des Inoperators überschreiben.

Funktion compileCode (src) {src = 'with (sandbox) {' + src + '}' const code = new Funktion ('sandbox', src) Rückgabefunktion (sandbox) {const sandboxProxy = neuer Proxy (sandbox, {has}) ) return code (sandboxProxy)}} // Dieser Trap fängt 'in'-Operationen auf dem Sandbox-Proxy ab. Funktion hat (Ziel, Schlüssel) {return true}

Der obige Code betrügt die mit Block schon die Variable im beim Sandkasten da wird es immer als wahr ausgewertet, weil die Falle hast du gibt immer true zurück. Der Code in der mit Block Es wird niemals versucht, auf das globale Objekt zuzugreifen.

Code-Auswertung in der Sandbox: 'with'-Anweisung und Proxys

Symbol.unscopables

Ein Symbol ist ein eindeutiger, unveränderlicher Datentyp und kann als Bezeichner für Objekteigenschaften verwendet werden.

Symbol.unscopables, es ist ein bekanntes Symbol. Ein bekanntes Symbol ist ein eingebautes JavaScript Symbol, que representa el comportamiento del idioma interno. Se pueden utilizar símbolos conocidos para agregar o sobrescribir iteraciones o comportamiento de Umwandlung primitivo, por ejemplo:

Das bekannte Symbol Symbol.unscopables Wird verwendet, um einen Objektwert anzugeben, dessen eigene und geerbte Eigenschaftsnamen von Umgebungsbindungen ausgeschlossen sind 'mit'.

Symbol.unscopables Definiert die nicht zu öffnenden Eigenschaften eines Objekts. Eigenschaften, die nicht erfasst werden können, werden in Deklarationen niemals aus dem Sandbox-Objekt abgerufen mit, Stattdessen werden sie direkt aus dem Abschluss oder dem globalen Bereich abgerufen. Symbol.unscopables Es ist eine sehr selten verwendete Funktion.

Wir können das obige Problem lösen, indem wir eine Falle definieren erhalten beim Sandbox-Proxy, die die Wiederherstellung von abfängt Symbol.unscopables und es gibt immer undefiniert zurück. Dies wird den Block täuschen mit so dass er denkt, dass unser Objekt von Sandkasten Es hat keine Eigenschaften, die nicht repariert werden können.



function compileCode (src) {
src = 'with (sandbox) {' + src + '}'
const code = new Function('sandbox', src)

return function (sandbox) {
const sandboxProxy = new Proxy(sandbox, {has, get})
return code(sandboxProxy)
}
}

function has (target, key) {
return true
}

function get (target, key) {
if (key === Symbol.unscopables) return undefined
return target[key]
}


WeakMaps para el almacenamiento en Zwischenspeicher

Der Code ist jetzt sicher, aber seine Leistung kann weiterhin aktualisiert werden, da bei jedem Aufruf des Codes ein neuer Proxy erstellt wird Rückgabefunktion. Dies kann vermieden werden, indem für jeden Funktionsaufruf mit demselben Sandbox-Objekt derselbe Proxy zwischengespeichert und verwendet wird.

Ein Proxy gehört zu einem Sandbox-Objekt, daher können wir den Proxy einfach als Eigenschaft zum Sandbox-Objekt hinzufügen. Dies würde jedoch unsere Implementierungsdetails der Öffentlichkeit zugänglich machen und würde im Fall eines stationären Objekts mit eingefrorenem Sandkasten nicht funktionieren Object.freeze (). Benutze einen Schwache Karte unds eine bessere Alternative in diesem Fall.

Das Objekt Schwache Karte ist eine Sammlung von Schlüssel / Wert-Paaren, in denen die Schlüssel schwach referenziert sind. Schlüssel müssen Objekte sein, und Werte können beliebige Werte sein.

Mit der WeakMap können Daten an ein Objekt angehängt werden, ohne es direkt um Eigenschaften zu erweitern. Wir können WeakMaps verwenden, um den Cache von Proxies indirekt zu Objekten in der Sandbox hinzuzufügen.



const sandboxProxies = new WeakMap()

function compileCode (src) {
src = 'with (sandbox) {' + src + '}'
const code = new Function('sandbox', src)

return function (sandbox) {
if (!sandboxProxies.has(sandbox)) {
const sandboxProxy = new Proxy(sandbox, {has, get})
sandboxProxies.set(sandbox, sandboxProxy)
}
return code(sandboxProxies.get(sandbox))
}
}

function has (target, key) {
return true
}

function get (target, key) {
if (key === Symbol.unscopables) return undefined
return target[key]
}


Auf diese Weise wird nur ein Proxy pro Sandbox-Objekt erstellt.

Das compileCode () Das obige Beispiel ist ein Workspace-Code-Evaluator, der nur mit 19 Codezeilen arbeitet.

Además de explicar la evaluación del código, el Ziel de este capítulo fue mostrar cómo se pueden usar las nuevas características de ES6 para alterar las existentes, en lugar de reinventarlas.