2010-08-09 9 views
6

Ich möchte eine nicht vertrauenswürdige .Lua-Datei in ihrer eigenen Umgebung ausführen, indem sie lua_setfenv() aufruft, so dass sie keinen von meinem Code beeinflussen kann.Wie führe ich eine nicht vertrauenswürdige Lua-Datei in ihrer eigenen Umgebung von der C-API aus

Die Dokumentation für diese Funktion erläutert jedoch nur, wie eine Funktion aufgerufen wird und nicht, wie eine Datei ausgeführt wird.

Zeit, die Datei auszuführen ich benutze:

int error = luaL_loadfile(mState, path.c_str()) || lua_pcall(mState, 0, 0, 0); 

Muß ich die „dofile“ lua-Funktion aus dem C-API mit lua_setfenv zu nennen, oder gibt es eine elegantere Möglichkeit, es zu tun?

Antwort

9

Siehe die Diskussion im Lua User's Wiki von sandboxing, und das allgemeinere Thema script security. Es gibt eine Reihe von subtilen und nicht so subtilen Problemen mit dieser Art von Dingen. Es kann getan werden, aber Schutz vor Code wie erfordert mehr als nur einzuschränken, welche Funktionen für eine Sandbox verfügbar sind.

Die allgemeine Technik besteht darin, eine Funktionsumgebung für die Sandbox zu erstellen, die eine Whitelist erlaubter Funktionen enthält. In einigen Fällen ist diese Liste möglicherweise sogar leer, aber es ist fast sicher harmlos, den Benutzer beispielsweise auf pairs() zugreifen zu lassen. Die Sandbox-Seite enthält eine Liste der Systemfunktionen, die nach ihrer Sicherheit aufgeschlüsselt sind, als eine praktische Referenz für die Erstellung einer solchen Whitelist.

Verwenden Sie dann lua_setfenv(), um die Funktionsumgebung auf das Skript des Benutzers anzuwenden, das Sie (falls noch nicht ausgeführt) mit lua_loadfile() oder lua_loadstring() entsprechend geladen haben. Mit der beigefügten Umgebung können Sie es mit lua_pcall() und Freunden ausführen. Vor der Ausführung haben einige Leute den geladenen Bytecode tatsächlich nach Operationen gescannt, die sie nicht zulassen wollen. Dies kann verwendet werden, um Schleifen oder Schreiben auf globale Variablen absolut zu verbieten.

Eine andere Anmerkung ist, dass die Ladefunktionen in der Regel entweder vorkompilierten Bytecode oder Lua-Text laden. Es erweist sich als viel sicherer, wenn Sie niemals vorkompilierten Bytecode zulassen, da eine Anzahl von Möglichkeiten identifiziert wurde, die dazu führen, dass das VM-Fehlverhalten entdeckt wird, die alle davon abhängen, dass ungültiger Bytecode manuell erzeugt wird. Da Bytecode-Dateien mit einer wohldefinierten Byte-Sequenz beginnen, die kein einfacher ASCII-Text ist, müssen Sie lediglich das Skript in einen String-Puffer lesen, auf das Fehlen des Markers prüfen und es nur an lua_loadstring() übergeben, wenn dies nicht der Fall ist Bytecode.

Es hat eine Menge Diskussionen über die Jahre dieser Art von Ding in der Lua-L mailing list, so dass die Suche nach dort ist wahrscheinlich auch hilfreich sein.

+0

Danke; Ich habe mich bereits um Endlosschleifen gekümmert, indem ich einen Debug-Hook gesetzt habe, der das Skript nach 10 Millionen Anweisungen beendet. Wie bei den Funktionen enthält die vom Benutzer bereitgestellte Datei nur globale Variablen und keinerlei Funktionen, so dass ich keine Funktion auf die weiße Liste setzen muss. –

5

By the way, das ist, was ich tun endete:

/* Loads, compiles and executes an unstrusted file. */ 
bool Lua::RunUntrustedFile(const string& path) 
{ 
    if(luaL_loadfile(mState, path.c_str())) 
    { 
     ErrorLog(lua_tostring(mState, 1)); 
     Pop(1); 
     return false; 
    } 

    Lua::SetMaximumInstructions(100000000); 
    lua_newtable(mState); 
    lua_setglobal(mState, "upload"); 
    ASSERT(Lua::GetStackSize() == 1); 
    lua_getglobal(mState, "upload"); 
    ASSERT_ALWAYS(lua_setfenv(mState, 1) != 0); 
    ASSERT(Lua::GetStackSize() == 1); 

    if(lua_pcall(mState, 0, 0, 0)) 
    { 
     Lua::ClearMaximumInstructions(); 
     ErrorLog(lua_tostring(mState, -1)); 
     Pop(1); 
     return false; 
    } 

    ASSERT(Lua::GetStackSize() == 0); 
    Lua::ClearMaximumInstructions(); 

    return true; 
} 

"Support" Funktionen:

static void Pop(int elements = 1) { lua_pop(mState, elements); } 

/* Sets a maximum number of instructions before throwing an error */ 
static void SetMaximumInstructions(int count) { 
    lua_sethook(mState, &Lua::MaximumInstructionsReached, LUA_MASKCOUNT, count); 
} 
static void ClearMaximumInstructions() { 
    lua_sethook(mState, &Lua::MaximumInstructionsReached, 0, 0); 
} 

static void MaximumInstructionsReached(lua_State *, lua_Debug *) 
{ 
    Error("The maximum number of instructions has been reached"); 
} 

static int GetStackSize() { return lua_gettop(mState); }