【问题标题】:Sandboxing Lua functions沙盒 Lua 函数
【发布时间】:2019-06-09 11:32:41
【问题描述】:

在我开始之前,让我澄清一下,我已经查看了有关沙盒的现有问题,但我发现的解决方案似乎都没有真正适合我。

我试图实现的不一定是一个完全安全的沙箱来运行不安全的代码,而是提供一个模拟游戏功能的环境(确切地说是战争中的星球大战帝国)我正在修改可以扩展使用 Lua 脚本。游戏提供了一堆我想在这个环境中实现的全局函数,以便能够在不启动游戏本身的情况下测试我的游戏扩展,因为它的调试能力很差。游戏也有一些怪癖。这种情况下的相关问题是使用 upvalues 会产生损坏的保存游戏,所以这就是为什么我们的 Lua 模块必须是全局的。由于我的游戏脚本不包含在单个文件中并且可能需要其他文件,因此使用 setfenv() 调用和声明它们自己的函数来设置环境对我来说并不真正有效,因为这仅适用于您指定的函数,而不适用于它可能调用的任何其他函数。

现在是我的代码本身。到目前为止,我已经尝试通过简单地深度克隆 _G 并在之后将其恢复到以前的状态来实现一个简单的沙箱(这仍然是 WIP,我意识到您仍然可以进行表和环境修改或可能突破沙箱此方法未涵盖的内容;正如我所说,这不是为了运行不安全的代码)。

这是在 sandbox.lua 中:

local function run(func)
    -- tested deep_clone with luassert's assert.are_same(), so this should be fine
    local g_clone = deep_clone(_G)
    -- coroutine is needed because one of the game functions needs
    -- to be able to interrupt the script
    coroutine.wrap(function()
        func()
    end)()

    for k, v in pairs(_G) do
        if not g_clone[k] then
            _G[k] = nil
        else
            _G[k] = g_clone[k]
        end
    end
end

这是我的测试脚本:

sandbox.run(function()
    require "src/declare_global" -- this declares A_GLOBAL = "TEST"
    print(A_GLOBAL) -- prints "TEST", everything is fine
end)

print(A_GLOBAL) -- prints nil, as intended

-- as I've restored _G to its former state I should be able to require 
-- the script again!
require "src/declare_global" 

print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- prints nil

print(A_GLOBAL) -- prints nil

正如您所看到的,我的问题是,在我在沙箱中完成后,需要仅声明全局的相同文件不再起作用。所以恢复_G的状态可能存在问题,但我不知道它可能是什么。不过,require 调用本身似乎有效。如果我需要的脚本返回一个函数,我仍然可以将它存储在一个变量中并执行它。

非常感谢有关如何解决此问题的任何建议。

【问题讨论】:

  • 如果这个沙箱不存在来运行不安全的代码......它的目标到底是什么?你需要什么来阻止函数执行?
  • 你是“深度克隆”_G。之后,您将从其副本“浅恢复”_G。之后,在您的全局环境表中,字段 _G 不等于表本身。这不好。
  • @NicolBolas - 您的问题的答案在 OP 的问题中。
  • @EgorSkriptunoff:我读了这个问题。它没有解释沙盒的目的是什么。它解释了沙盒应该是什么样子以及脚本不能使用本地变量,但没有说明沙盒应该阻止哪些操作。
  • @NicolBolas - 最好将“沙盒”一词替换为“能够在测试后恢复全局变量的测试/调试环境”。

标签: lua


【解决方案1】:

我找到了解决问题的方法。但是,我觉得这是实现我想要的效率低下的方式,所以如果有人有更好的建议,我很乐意听到。

@EgorSkriptunoff 对我关于“浅”恢复 _G 的原始问题的评论为我指明了正确的方向,所以我最终编写了一个“深度恢复”功能,这似乎与我迄今为止测试过的功能相得益彰.

local known_tables = {}

local function deep_restore(tab, backup)
    known_tables[tab] = true
    for k, v in pairs(tab) do
        if backup[k] == nil then
            tab[k] = nil
        elseif type(v) == "table" and not known_tables[v] then
            deep_restore(v, backup[k])
        elseif not type(v) == "table" then
            tab[k] = backup[k]
        end
    end
end


local function run(func)
    local g_clone = deep_clone(_G)

    coroutine.wrap(function()
        func()
    end)()

    deep_restore(_G, g_clone)
    known_tables = {}

end

结果:

sandbox.run(function()
    require "src/declare_global"
    print("Printing inside sandbox: "..A_GLOBAL) -- prints TEST
end)

print("This should be nil: "..tostring(A_GLOBAL)) -- nil
print("package.loaded should be nil: "..tostring(package.loaded["src/declare_global"])) -- nil

print("\nRequiring again...")
require "src/declare_global"
print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- true
print(A_GLOBAL) -- TEST

【讨论】:

    猜你喜欢
    • 2017-05-12
    • 2012-07-02
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 2020-03-10
    • 2016-04-21
    相关资源
    最近更新 更多