如何创建安全的Lua沙箱?

时间:2009-08-03 21:23:45

标签: lua sandbox

所以Lua似乎是在我的应用程序中实现安全“用户脚本”的理想选择。

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

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

如何删除/阻止这些不安全的函数,而不仅仅是一个甚至没有“ipairs”函数等基本内容的解释器?

7 个答案:

答案 0 :(得分:53)

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

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

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

有关lua沙盒的详细信息,请参阅Lua Sandboxes

答案 1 :(得分:31)

以下是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)

答案 2 :(得分:14)

Lua live demo包含(专用)沙箱。 source免费提供。

答案 3 :(得分:4)

清除不受欢迎的人的最简单方法之一是首先加载你自己设计的Lua脚本,其中包括:

load = nil
loadfile = nil
dofile = nil

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

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

答案 4 :(得分:3)

您可以使用Lua API提供的lua_setglobal函数将全局命名空间中的值设置为nil,这将有效阻止任何用户脚本访问它们。

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

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

...etc...

答案 5 :(得分: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

答案 6 :(得分:-2)

您可以覆盖(禁用)所需的任何Lua功能,也可以使用metatables获取更多控件