代码之家  ›  专栏  ›  技术社区  ›  Andreas Bonini

如何从C API在自己的环境中执行不受信任的Lua文件

  •  6
  • Andreas Bonini  · 技术社区  · 15 年前

    我想通过调用 lua_setfenv() 所以它不会影响我的任何代码。

    不过,该函数的文档只解释了如何调用函数,而不是如何执行文件。

    当前要运行我使用的文件:

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

    lua_setfenv ,还是有更优雅的方法?

    3 回复  |  直到 15 年前
        1
  •  9
  •   RBerteig Keith Adler    14 年前

    请参阅的Lua用户Wiki上的讨论 sandboxing ,以及 script security . 这类事情有很多微妙而不那么微妙的问题。这是可以做到的,但是要防止 for i=1,1e39 do end 需要的不仅仅是限制沙盒可用的函数。

    一般技术是为沙盒创建一个函数环境,其中包含一个允许函数的白名单。在某些情况下,该列表甚至可能为空,但允许用户访问 pairs()

    然后使用 lua_setfenv() 将函数环境应用于加载(但尚未执行)的用户脚本 lua_loadfile() lua_loadstring() 视情况而定。连接环境后,可以使用 lua_pcall() 还有朋友。在执行之前,有些人实际上已经扫描了加载的字节码,寻找他们不想允许的操作。可以用来绝对禁止循环或写入全局变量。

    另一个注意事项是,load函数通常会加载预编译的字节码或Lua文本。如果不允许预编译字节码,结果会安全得多,因为已经确定了许多使VM行为异常的方法,这些方法都依赖于手工生成无效的字节码。由于字节码文件以一个定义良好的字节序列开始,而该字节序列不是纯ASCII文本,因此您所需要做的就是将脚本读入字符串缓冲区,测试是否缺少标记,并只将其传递给 lua\u loadstring()

    会议上进行了相当多的讨论 Lua-L mailing list 多年来这种事情,所以在那里搜索也可能是有帮助的。

        2
  •  5
  •   Andreas Bonini    14 年前

    顺便说一句,这就是我最后要做的:

    /* 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;
    }
    

    “支持”功能:

    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); }
    
        3
  •  2
  •   Community CDub    8 年前

    luaL_loadfile() 将加载块,然后调用 lua_setfenv() lua_pcall() 执行区块。见梅加登法官最近的回答 Calling lua functions from .lua's using handles?