LuaSandbox
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 dasio
-Paket, da sie direkten Zugriff auf das Dateisystem erlauben. Falls nötig, sollte der Dateisystemzugriff durch PHP-Callbacks erfolgen.- Das
package
-Paket, inklusiverequire()
undmodule()
, da es sehr auf direkten Dateisystemzugriff angewiesen ist. Eine rein-Lua-Umschreibung, wie dies in Scribunto verwendet wird, kann stattdessen verwendet werden. load()
undloadstring()
, 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()
undos.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 dascoroutine
-Paket wurden nicht auf Sicherheit überprüft.
Die folgenden Funktionen wurden modifiziert:
pcall()
undxpcall()
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()
undmath.randomseed()
werden durch Versionen ersetzt, die den Status nicht mit PHPsrand()
teilen.- Die Lua 5.2 Metamethoden
__pairs
und__ipairs
werden vonpairs()
undipairs()
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.