75

所以 Lua 似乎非常适合在我的应用程序中实现安全的“用户脚本”。

但是,嵌入 lua 的大多数示例似乎都包括加载所有标准库,包括“io”和“package”。

所以我可以从我的解释器中排除这些库,但即使是基本库也包括访问文件系统的函数“dofile”和“loadfile”。

我怎样才能删除/阻止任何像这样的不安全功能,而不仅仅是最终得到一个甚至没有像“ipairs”功能这样的基本东西的解释器?

4

7 回答 7

53

您可以通过setfenv () 设置运行不受信任代码的函数环境。这是一个大纲:

local env = {ipairs}
setfenv(user_script, env)
pcall(user_script)

user_script函数只能访问其环境中的内容。因此,您可以显式添加您希望不受信任的代码可以访问的功能(白名单)。在这种情况下,用户脚本只能访问ipairs但没有其他权限(dofileloadfile等)。

有关lua 沙盒的示例和更多信息,请参阅 Lua 沙盒。

于 2009-08-03T21:37:00.960 回答
35

这是 Lua 5.2 的解决方案(包括一个也适用于 5.1 的示例环境):

-- save a pointer to globals that would be unreachable in sandbox
local e=_ENV

-- sample sandbox environment
sandbox_env = {
  ipairs = ipairs,
  next = next,
  pairs = pairs,
  pcall = pcall,
  tonumber = tonumber,
  tostring = tostring,
  type = type,
  unpack = unpack,
  coroutine = { create = coroutine.create, resume = coroutine.resume, 
      running = coroutine.running, status = coroutine.status, 
      wrap = coroutine.wrap },
  string = { byte = string.byte, char = string.char, find = string.find, 
      format = string.format, gmatch = string.gmatch, gsub = string.gsub, 
      len = string.len, lower = string.lower, match = string.match, 
      rep = string.rep, reverse = string.reverse, sub = string.sub, 
      upper = string.upper },
  table = { insert = table.insert, maxn = table.maxn, remove = table.remove, 
      sort = table.sort },
  math = { abs = math.abs, acos = math.acos, asin = math.asin, 
      atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos, 
      cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor, 
      fmod = math.fmod, frexp = math.frexp, huge = math.huge, 
      ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max, 
      min = math.min, modf = math.modf, pi = math.pi, pow = math.pow, 
      rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh, 
      sqrt = math.sqrt, tan = math.tan, tanh = math.tanh },
  os = { clock = os.clock, difftime = os.difftime, time = os.time },
}

function run_sandbox(sb_env, sb_func, ...)
  local sb_orig_env=_ENV
  if (not sb_func) then return nil end
  _ENV=sb_env
  local sb_ret={e.pcall(sb_func, ...)}
  _ENV=sb_orig_env
  return e.table.unpack(sb_ret)
end

然后要使用它,您将调用您的函数 ( my_func),如下所示:

pcall_rc, result_or_err_msg = run_sandbox(sandbox_env, my_func, arg1, arg2)
于 2011-08-08T12:25:49.183 回答
14

Lua 现场演示包含一个(专门的)沙箱。是免费的。

于 2009-08-04T01:31:09.273 回答
5

清除不受欢迎的最简单方法之一是首先加载您自己设计的 Lua 脚本,它执行以下操作:

load = nil
loadfile = nil
dofile = nil

或者,您可以使用setfenv创建一个受限环境,您可以将特定的安全功能插入其中。

完全安全的沙盒有点难。如果您从任何地方加载代码,请注意预编译的代码可能会使 Lua 崩溃。如果您没有关闭它的系统,即使是完全受限的代码也可能进入无限循环并无限期阻塞。

于 2009-08-04T00:22:17.597 回答
3

您可以使用lua_setglobalLua API 提供的函数在全局命名空间中设置这些值,nil从而有效地阻止任何用户脚本访问它们。

lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "io");

lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "loadfile");

...etc...
于 2009-08-03T21:33:39.500 回答
1

如果您使用的是 Lua 5.1,请尝试以下操作:

blockedThings = {'os', 'debug', 'loadstring', 'loadfile', 'setfenv', 'getfenv'}
scriptName = "user_script.lua"

function InList(list, val) 
    for i=1, #list do if list[i] == val then 
        return true 
    end 
end

local f, msg = loadfile(scriptName)

local env = {}
local envMT = {}
local blockedStorageOverride = {}
envMT.__index = function(tab, key)
    if InList(blockedThings, key) then return blockedStorageOverride[key] end
    return rawget(tab, key) or getfenv(0)[key]
end
envMT.__newindex = function(tab, key, val)
    if InList(blockedThings, key) then
        blockedStorageOverride[key] = val
    else
        rawset(tab, key, val)
    end
end

if not f then
    print("ERROR: " .. msg)
else
    setfenv(f, env)
    local a, b = pcall(f)
    if not a then print("ERROR: " .. b) end
end
于 2013-10-07T03:09:59.707 回答
-2

您可以覆盖(禁用)您想要的任何 Lua 函数,也可以使用元表进行更多控制

于 2009-08-03T21:34:31.010 回答