2010-08-09 6 views
6

Je souhaite exécuter un fichier .lua non approuvé dans son propre environnement en appelant lua_setfenv() afin qu'il ne puisse affecter aucun de mes codes.Comment exécuter un fichier Lua non sécurisé dans son propre environnement à partir de l'API C

La documentation de cette fonction explique cependant comment appeler une fonction et non comment exécuter un fichier.

Actuellement pour exécuter le fichier que j'utilise:

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

Dois-je appeler la fonction lua « dofile » de l'API C avec lua_setfenv, ou est-il une façon plus élégante de le faire?

Répondre

9

Voir la discussion au Wiki de l'utilisateur Lua de sandboxing, et le sujet plus général de script security. Il y a un certain nombre de problèmes subtils et pas si subtils avec ce genre de chose. Cela peut être fait, mais la protection contre un code tel que for i=1,1e39 do end nécessite plus qu'une simple restriction des fonctions disponibles pour un sandbox.

La technique générale consiste à créer un environnement de fonction pour le bac à sable contenant une liste blanche de fonctions autorisées. Dans certains cas, cette liste peut même être vide, mais laisser l'utilisateur accéder à pairs(), par exemple, est presque certainement inoffensif. La page sandbox contient une liste des fonctions système décomposées par leur sécurité en tant que référence pratique pour la construction d'une telle liste blanche.

Vous utilisez ensuite lua_setfenv() pour appliquer l'environnement de fonction au script de l'utilisateur que vous avez chargé (mais pas encore exécuté) avec lua_loadfile() ou lua_loadstring() selon le cas. Avec l'environnement ci-joint, vous pouvez l'exécuter avec lua_pcall() et vos amis. Avant l'exécution, certaines personnes ont scanné le bytecode chargé des opérations qu'ils ne veulent pas autoriser. Cela peut être utilisé pour interdire absolument les boucles ou écrire dans des variables globales.

Une autre remarque est que les fonctions de chargement chargeront généralement le code par défaut précompilé ou le texte Lua. Il s'avère être beaucoup plus sûr si vous n'autorisez jamais le bytecode précompilé, car un certain nombre de façons de rendre la machine défectueuse ont été identifiées comme si tout dépendait d'un bytecode invalide. Comme les fichiers bytecode commencent par une séquence d'octets bien définie qui n'est pas du texte ASCII, il vous suffit de lire le script dans un tampon de chaînes, de tester l'absence du marqueur et de le passer à lua_loadstring() s'il ne l'est pas. bytecode.

Il y a eu une bonne quantité de discussion au Lua-L mailing list au cours des années de ce genre de chose, donc la recherche là-bas devrait également être utile.

+0

vous remercie; J'ai déjà pris en charge les boucles infinies en définissant un hook de débogage qui termine le script après 10 millions d'instructions. En ce qui concerne les fonctions, le fichier fourni par l'utilisateur ne contient que des variables globales et aucune fonction, donc je n'ai pas à ajouter de fonction à la liste blanche. –

2

luaL_loadfile() charge le bloc, puis appelle le lua_setfenv() pour définir la table d'environnement, puis appelle le lua_pcall() pour exécuter le tronçon. Voir la réponse récente donnée par le juge Maygarden au Calling lua functions from .lua's using handles?

5

Par ailleurs, ce que je fini par faire:

/* 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" Fonctions:

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); }