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

时间:2010-08-09 21:20:18

标签: c++ c lua lua-api

我想通过调用lua_setfenv()在自己的环境中执行不受信任的.lua文件,这样它就不会影响我的任何代码。

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

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

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

我是否必须使用lua_setfenv从C API调用“dofile”lua函数,还是有更优雅的方法来执行此操作?

3 个答案:

答案 0 :(得分:9)

请参阅Lua用户Wiki的sandboxing讨论以及script security的更一般主题。这种事情有许多微妙而不那么微妙的问题。可以这样做,但是对for i=1,1e39 do end之类的代码进行保护不仅需要限制沙盒可用的功能。

一般技术是为沙箱创建一个功能环境,其中包含允许函数的白名单。在某些情况下,该列表甚至可能是空的,但是让用户访问pairs()几乎肯定是无害的。沙箱页面有一个系统功能列表,按其安全性细分,作为构建此类白名单的便捷参考。

然后使用lua_setfenv()将功能环境应用于您加载(但尚未执行)的用户脚本(适用于lua_loadfile()lua_loadstring())。附加环境后,您可以使用lua_pcall()和朋友执行它。在执行之前,有些人实际上已经扫描了加载的字节码以查找他们不想允许的操作。这可以用来绝对禁止循环或写入全局变量。

另一个注意事项是加载函数通常会加载预编译的字节码或Lua文本。如果你从不允许预编译的字节码,那么结果会更加安全,因为已经发现许多使行为不正常的方法都依赖于手工制作无效的字节码。由于字节码文件以明确定义的字节序列开头,而不是纯ASCII文本,所以您需要做的就是将脚本读入字符串缓冲区,测试标记的缺失,并仅将其传递给lua_loadstring()如果它不是字节码。

这些年来Lua-L mailing list多年来一直在进行相当多的讨论,所以在那里搜索也可能会有所帮助。

答案 1 :(得分:5)

顺便说一下,这就是我最终做的事情:

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

答案 2 :(得分:2)

luaL_loadfile()将加载块,然后调用lua_setfenv()来设置环境表,然后调用lua_pcall()来执行块。请参阅Maygarden法官在Calling lua functions from .lua's using handles?

给出的最新答案