Jump to content

LuaSandbox

From mediawiki.org
This page is a translated version of the page LuaSandbox and the translation is 100% complete.
Für die MediaWiki-Erweiterung, die es erlaubt, Sandbox-Lua-Code zu verwenden siehe: Extension:Scribunto.

LuaSandbox ist eine Erweiterung für PHP 7 und PHP 8, die es ermöglicht, nicht vertrauenswürdigen Lua 5.1 Code sicher innerhalb von PHP auszuführen, was in der Regel schneller ist, als eine Lua-Binärdatei auszuliefern und Interprozesskommunikation zu nutzen.

Installation

Vorverpackt

LuaSandbox ist in Debian 10 und Ubuntu 18.04 und neuer verfügbar. Installiere mit den folgenden Befehl:

apt install php-luasandbox -y

PECL

LuaSandbox ist jetzt in PECL verfügbar, das auch vorgefertigte Windows-DLLs bereitstellt. Siehe unsere Paketseite. Lade erstmal die richtige Lua 5.1-Bibliothek herunter, wie unten bei "Manuelle Installation" beschrieben. Führe dann folgendes aus:

pecl install luasandbox

Manuelle Installation

Voraussetzungen

Installiere die Header- und Bibliothekdateien für PHP und Lua 5.1.

  • Für Debian-derivative Linux-Distributionen wie Ubuntu:
    apt install php-dev liblua5.1-0-dev -y
    
  • Für CentOS/Redhat-derivative Linux-Distributionen:
    yum install php-devel lua5.1 lua5.1-devel
    
  • Für macOS
    brew install lua
    

Herunterladen

Lade den Quellcode in ein geeignetes Verzeichnis von git herunter:

git init
git pull https://gerrit.wikimedia.org/r/mediawiki/php/luasandbox.git

Oder lade einen Snapshot herunter und entpacke es.

Builden

luasandbox hier ist das Verzeichnis, in das das LuaSandbox Git-Repository geklont wurde.

cd luasandbox
phpize && ./configure && make && sudo make install

Füge dann extension=luasandbox.so an geeigneter Stelle in die PHP-Konfiguration ein. In modernen, von Debian abgeleiteten Distributionen würdest du zum Beispiel eine Datei zu /etc/php/$version/mods-available hinzufügen (wobei $version die PHP-Version ist, für die du LuaSandbox kompiliert hast) und den Befehl phpenmod verwenden, um sie zu aktivieren.

Wenn du LuaSandbox mit einer Webanwendung wie MediaWiki verwendest, musst du deinen Webserver neu starten oder php-fpm für PHP, um die Erweiterung zu laden. Nach einem solchen Neuladen solltest du LuaSandbox in der Ausgabe von phpinfo() und get_loaded_extensions() (und bei MediaWiki mit installiertem Scribunto auch in Special:Version) sehen.

Beispiele

$sandbox = new LuaSandbox;
$sandbox->setMemoryLimit( 50 * 1024 * 1024 );
$sandbox->setCPULimit( 10 );

// Registriere ein paar Funktionen in der Lua-Umgebung

function frobnosticate( $v ) {
    return [ $v + 42 ];
}

$sandbox->registerLibrary( 'php', [
    'frobnosticate' => 'frobnosticate',
    'output' => function ( $string ) {
        echo "$string\n";
    },
    'error' => function () {
        throw new LuaSandboxRuntimeError( "Irgendwas stimmt nicht" );
    }
] );

// Ausführen von Lua-Code, einschließlich Rückrufe in PHP und Lua

$luaCode = <<<EOF
php.output( "Hello, world" );

return "Hi", function ( v )
    return php.frobnosticate( v + 200 )
end
EOF;

list( $hi, $frob ) = $sandbox->loadString( $luaCode )->call();
assert( $frob->call( 4000 ) === [ 4242 ] );

// Von PHP ausgelöste LuaSandboxRuntimeError-Ausnahmen können in Lua abgefangen werden

list( $ok, $message ) = $sandbox->loadString( 'return pcall( php.error )' )->call();
assert( !$ok );
assert( $message === 'Something is wrong' );

Dokumentation

Unsere Dokumentation befindet sich jetzt im Upstream-PHP-Handbuch unter https://www.php.net/book.luasandbox.

Wenn du das Handbuch ändern möchtest, kannst du entweder einen Pull-Request gegen das PHP-Handbuch-Repository auf GitHub einreichen oder du kannst unseren Spiegel des LuaSandbox-Kapitels im Gerrit-Projekt der Erweiterung ändern.

Unterschiede zum Standard-Lua

LuaSandbox bietet eine Sandbox-Umgebung, die sich in einigen Punkten vom Standard-Lua 5.1 unterscheidet.

Die folgenden Funktionen und Pakete sind nicht verfügbar:

  • dofile(), loadfile() und das io-Paket, da sie direkten Zugriff auf das Dateisystem erlauben. Falls nötig, sollte der Dateisystemzugriff durch PHP-Callbacks erfolgen.
  • Das package-Paket, inklusive require() und module(), da es sehr auf direkten Dateisystemzugriff angewiesen ist. Eine rein-Lua-Umschreibung, wie dies in Scribunto verwendet wird, kann stattdessen verwendet werden.
  • load() und loadstring(), um eine statische Analyse von Lua-Code zu erlauben.
  • print(), da dies zur Standardausgabe ausgibt. Falls nötig, sollte die Ausgabe durch PHP-Callbacks erfolgen.
  • Der Großteil des os-Pakets, da es die Manipulation des Prozesses und das Ausführen von anderen Prozessen erlaubt.
    • os.clock(), os.date(), os.difftime() und os.time() bleiben verfügbar.
  • Der Großteil des debug-Pakets, da es die Manipulation des Lua-Status und der Metadaten auf Weisen ermöglicht, die das Sandboxing brechen kann.
    • debug.traceback() bleibt verfügbar.
  • string.dump(), da es möglicherweise interne Daten offenlegt.
  • collectgarbage(), gcinfo() und das coroutine-Paket wurden nicht auf Sicherheit überprüft.

Die folgenden Funktionen wurden modifiziert:

  • pcall() und xpcall() können bestimmte Fehler nicht fangen, insbesondere Zeitüberschreitungsfehler.
  • tostring() enthält keine Zeigeradressen.
  • string.match() wurde gepatcht, um die Rekursionstiefe zu begrenzen und regelmäßig auf einen Timeout zu prüfen.
  • math.random() und math.randomseed() werden durch Versionen ersetzt, die den Status nicht mit PHPs rand() teilen.
  • Die Lua 5.2 Metamethoden __pairs und __ipairs werden von pairs() und ipairs() unterstützt.

Geschichte

Im Laufe der Jahre bekam die Wikitext-Vorlagensprache von MediaWiki mehr Funktionen und wurde immer komplizierter. Bereits 2009 begannen die MediaWiki-Entwickler, die Idee zu diskutieren, eine echte Skriptsprache einzubetten, anstatt den Wikitext immer zu verkomplizieren.

Zu den Anforderungen für ein solches Projekt gehörten eine starke Sandbox und strenge Beschränkungen für die Nutzung von Speicher und CPU-Zeit, da es nicht vertrauenswürdigen Benutzercode auf Produktionsservern ausführen würde. Es müsste als eigenständige Binärdatei nutzbar sein, wobei die Möglichkeit, sie über eine PHP-Erweiterung im Prozess laufen zu lassen, um die Leistung zu verbessern, ein großer Vorteil wäre.

Als die Entwicklung etwa 2011 ernsthaft begann, gab es vier mögliche Sprachen: Lua, JavaScript, PHP oder eine hypothetische, noch zu entwickelnde "WikiScript"-Sprache. Lua hatte verschiedene Vorteile:

  • Klein (eigenständig 170K) und schnell. Auch die Existenz von LuaJIT wurde als Vorteil betrachtet.
  • Konzipiert für die Einbettung, einschließlich einfacher Hooks für CPU- und Arbeitsspeicherbegrenzung.
  • Einfaches Sandboxing, keine internen Globals.
  • Detaliertes Referenzhandbuch, inklusive Anweisungen zum Einbetten.

Der Hauptnachteil war, dass es nicht so bekannt war wie JavaScript.

JavaScript hatte in der damaligen Form der V8-Engine mehrere Nachteile:

  • Minimale Dokumentation zur Einbettung.
  • Fortgesetzte Unterstützung für die Einbettung unklar.
  • Kein Zuordnungshook.
  • Riesige eigenständige Binärdatei.

Die Rhino-Engine war noch schlimmer, da sie in Java geschrieben wurde und nicht in PHP eingebettet werden konnte. PHP selbst wurde verworfen, da eine korrekte Einbettung und Sandboxing extrem schwierig und das Pre-Parsing langsam gewesen wäre, und "WikiScript" wäre ein viel größeres Projekt gewesen, da man einen Interpreter (oder zwei) von Grund auf hätte entwickeln müssen.

Daher wurde Lua gewählt, genauer gesagt die damals verfügbare Version 5.1, und diese PHP-Erweiterung entwickelt. Die Änderungen, die in 5.2 an der Handhabung der Funktionsumgebung vorgenommen wurden, haben ein einfaches Upgrade seither verhindert, siehe phab:T178146 für Details.