6

I want to execute an untrusted .lua file in its own environment by calling lua_setfenv() so that it cannot affect any of my code.

The documentation for that function though only explains how to call a function, not how to execute a file.

Currently to run the file I use:

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

Do I have to call the "dofile" lua function from the C API with lua_setfenv, or is there a more elegant way to do it?

4

3 回答 3

9

请参阅 Lua 用户的沙盒Wiki 上的讨论,以及更一般的脚本安全主题。这种事情有许多微妙的而不是那么微妙的问题。这是可以做到的,但要防范诸如此类的代码for i=1,1e39 do end需要的不仅仅是限制沙盒可用的功能。

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

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

另一个注意事项是加载函数通常会加载预编译的字节码或 Lua 文本。如果你永远不允许预编译的字节码,事实证明会更安全,因为已经确定了许多使 VM 行为不端的方法,这些方法都依赖于手工制作的无效字节码。由于字节码文件以非纯 ASCII 文本的明确字节序列开头,因此您只需将脚本读入字符串缓冲区,测试是否缺少标记,lua_loadstring()如果不是字节码,则仅将其传递给.

多年来,在Lua-L 邮件列表中已经对这类事情进行了大量讨论,因此在那里搜索也可能会有所帮助。

于 2010-08-09T23:50:00.990 回答
5

By the way, this is what I ended up doing:

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

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); }
于 2010-11-08T21:25:03.307 回答
2

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

于 2010-08-09T22:17:59.307 回答