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

2021-12-30 00:00:00 c lua c++ lua-api

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

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

我是否必须使用 lua_setfenv 从 C API 调用dofile"lua 函数,或者有更优雅的方法吗?

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?

推荐答案

参见 Lua User's Wiki 中 沙盒,以及脚本安全的更一般的主题.这种事情有许多微妙的和不那么微妙的问题.这是可以做到的,但是防止诸如 for i=1,1e39 do end 之类的代码需要的不仅仅是限制沙箱可用的功能.

See the discussion at the Lua User's Wiki of sandboxing, and the more general topic of script security. There are a number of subtle and not so subtle issues with this kind of thing. It can be done, but protecting against code such as for i=1,1e39 do end requires more than just restricting what functions are available to a sandbox.

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

The general technique is to create a function environment for the sandbox that has a whitelist of permitted functions in it. In some cases, that list might even be empty, but letting the user have access to pairs(), for example, is almost certainly harmless. The sandbox page has a list of the system functions broken down by their safety as a handy reference for constructing such a whitelist.

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

You then use lua_setfenv() to apply the function environment to the user's script which you loaded (but haven't yet executed) with lua_loadfile() or lua_loadstring() as appropriate. With the environment attached, you could execute it with lua_pcall() and friends. Before execution, some people have actually scanned the loaded bytecode for operations that they don't want to permit. That can be used to absolutely forbid loops or writing to global variables.

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

One other note is that the load functions will generally load either precompiled bytecode or Lua text. It turns out to be a lot safer if you never permit precompiled bytecode, as a number of ways to make the VM misbehave have been identified that all depend on handcrafting invalid bytecode. Since bytecode files begin with a well-defined byte sequence that is not plain ASCII text, all you need to do is read the script into a string buffer, test for the absense of the marker, and only pass it to lua_loadstring() if it is not bytecode.

在 Lua-L 邮件列表上进行了大量的讨论.这种事情的年代,所以在那里搜索也可能会有所帮助.

There has been a fair amount of discussion at the Lua-L mailing list over the years of this kind of thing, so searching there is also likely to be helpful.

相关文章