Как я могу создать безопасную песочницу Lua? - PullRequest
72 голосов
/ 04 августа 2009

Так что Lua кажется идеальным для реализации безопасных «пользовательских скриптов» внутри моего приложения.

Однако большинство примеров встраивания lua, похоже, включают загрузку всех стандартных библиотек, включая «io» и «package».

Таким образом, я могу исключить эти библиотеки из моего интерпретатора, но даже базовая библиотека включает функции «dofile» и «loadfile», которые обращаются к файловой системе.

Как я могу удалить / заблокировать любые небезопасные функции, подобные этим, не прибегая к помощи интерпретатора, у которого даже нет таких базовых вещей, как функция "ipairs"?

Ответы [ 7 ]

53 голосов
/ 04 августа 2009

Вы можете установить функциональную среду, в которой запускаете ненадежный код, с помощью setfenv (). Вот схема:

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

Функция user_script может получить доступ только к тому, что находится в ее окружении. Таким образом, вы можете явно добавить функции, к которым у ненадежного кода должен быть доступ (белый список). В этом случае пользовательский сценарий имеет доступ только к ipairs, но ничего больше (dofile, loadfile и т. Д.).

См. Lua Sandboxes для примера и дополнительной информации о lua sandboxing.

31 голосов
/ 08 августа 2011

Вот решение для 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)
14 голосов
/ 04 августа 2009

Демо Lua live содержит (специализированную) песочницу. источник находится в свободном доступе.

4 голосов
/ 04 августа 2009

Один из самых простых способов убрать нежелательное - это сначала загрузить скрипт Lua по вашему собственному замыслу, который выполняет такие вещи, как:

load = nil
loadfile = nil
dofile = nil

В качестве альтернативы вы можете использовать setfenv для создания ограниченной среды, в которую вы можете вставлять определенные безопасные функции.

Абсолютно безопасная песочница немного сложнее. Если вы загружаете код из любого места, помните, что предварительно скомпилированный код может привести к сбою Lua. Даже полностью ограниченный код может войти в бесконечный цикл и блокироваться на неопределенный срок, если у вас нет системы для его выключения.

3 голосов
/ 04 августа 2009

Вы можете использовать функцию lua_setglobal, предоставляемую API Lua, чтобы установить для этих значений в глобальном пространстве имен значение nil, что будет эффективно препятствовать доступу к ним всех пользовательских скриптов.

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

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

...etc...
1 голос
/ 07 октября 2013

Если вы используете 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
0 голосов
/ 04 августа 2009

Вы можете переопределить (отключить) любую функцию Lua, которую вы хотите, а также вы можете использовать metatables для более управления .

...