我正在尝试使用How can I create a secure Lua sandbox?构建我自己的漏洞沙箱。
我正在尝试创建一个Lua沙箱,其中一些Lua函数可以访问沙箱外的其他一些Lua函数。例如,我希望我的沙箱有一个特殊的“显示”功能,可以调用“打印”但在沙箱中也没有“打印”。
主要问题是我正在尝试在已经很大的代码库中构建一个沙箱,所以我不能忽略功能。
这怎么可能?
由于没有我的错误,解决方案必须是纯粹的Lua函数。
答案 0 :(得分:5)
创建沙箱时,可以通过从较大的环境中挑选函数和值来创建新的沙箱环境。你不需要在原始环境中销毁或“清除”任何东西。
所以,
local script = loadstring "display(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)
打印
0.69314718055995
而
local script = loadstring "print(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)
失败
false [string "print(math.log(2, 3))"]:1: attempt to call global 'print' (a nil value)
答案 1 :(得分:1)
是否需要调用Lua标准库print
函数?您可以改为模仿print
的功能吗?因为这是最简单的方法。
但是,如果你想拥有print
的包装器,有两种方法可以做到:使用纯Lua代码和C / C ++代码。
纯Lua溶液如下。请注意,这应该在加载任何外部脚本之前完成。首先,打开其中包含print
的Lua标准库。然后运行这个Lua脚本:
local internal_print = print
return function(...)
--Do display logic.
internal_print(...) --Or whatever else you want.
end
这将返回“显示”功能。如果您愿意,可以将其存储在名为display
的全局变量中,或者调用其他内容。
之后,您可以nil
print
全局变量,从而使其几乎完全无法访问。
如果你想用C / C ++来做,那就非常相似了。首先,和以前一样,您注册包含print
的Lua标准库,以便您可以获得它的功能。然后,使用lua_getglobal(L, "print")
获取print
函数并将其推入堆栈。接下来,使用lua_pushcclosure
注册C / C ++函数。但是你想指定一个upvalue,Lua会在注册时弹出堆栈。
现在你的注册函数在堆栈中,等待被推入Lua变量或全局表项。
警告: Lua调试库可以查看upvalues,从而从新函数中获取print
函数。因此,如果您想要完美的安全性,请摆脱debug.getupvalue
。
答案 2 :(得分:0)
构建沙箱(或多个沙箱,如果它们各自有不同的要求),并将不受信任的代码一次一个地移动到沙箱中。在我的快速cli测试中,5.1和5.2都将运行在沙箱之外定义的函数而不进行修改。要使用Doug的示例,假设display
是使用print
的预先存在的代码的一部分:
-- 5.1
local function display(...)
print(...)
end
local script = loadstring "display(math.log(2, 3))"
local env = {display = display, math = math, string = string}
setfenv(script, env)
print(pcall(script))
-- 5.2
local function display(...)
print(...)
end
local script = loadstring "display(math.log(2, 3))"
local e=_ENV
_ENV={display = display, math = math, string = string}
e.print(e.pcall(script))
_ENV=e
请注意,在上述两个示例中,display
函数正在使用print
而不修改该代码,因为在创建此函数时您不在沙箱中。
在过去,我已经存储了一个指向非沙盒环境的本地指针,但是我无法重现我在快速cli测试中需要的情况。如果你能想出一个例子,我可能会想出一个不需要e
变量的解决方法。以下是使用5.2的代码示例:
local e=_ENV
for k,v in e.pairs(value) do
-- iterate
end
另一个例子,对于我的只读表格代码,我再次使用e
:
function ro_table (t)
local t = t
if t then
return e.setmetatable({},
{ __index=t,
__newindex= function(_,_,_) e.error ("Attempt to modify read-only table") end,
})
else
return nil
end
end