我想通过调用lua_setfenv()在自己的环境中执行不受信任的.lua文件,这样它就不会影响我的任何代码.
该功能的文档虽然只解释了如何调用函数,而不是如何执行文件.
目前运行我使用的文件:
int error = luaL_loadfile(mState, path.c_str()) || lua_pcall(mState, 0, 0, 0);
Run Code Online (Sandbox Code Playgroud)
我是否必须使用C API调用"dofile"lua函数lua_setfenv,或者是否有更优雅的方法来执行此操作?
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(),例如,几乎肯定是无害的.沙箱页面包含系统功能列表,这些功能按其安全性细分,作为构建此类白名单的便捷参考.
然后,使用lua_setfenv()与应用功能环境,您加载(但尚未执行)用户的脚本lua_loadfile()或lua_loadstring()适当的.附加环境后,您可以lua_pcall()和朋友一起执行它.在执行之前,有些人实际上已经扫描了加载的字节码以查找他们不想允许的操作.这可以用于绝对禁止循环或写入全局变量.
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.
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.
顺便说一下,这就是我最终做的事情:
/* 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;
}
Run Code Online (Sandbox Code Playgroud)
"支持"功能:
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); }
Run Code Online (Sandbox Code Playgroud)