Как создать безопасную песочницу Lua?
Итак, Lua кажется идеальным для реализации безопасных "пользовательских скриптов" внутри моего приложения.
Однако большинство примеров внедрения lua, похоже, включают загрузку всех стандартных библиотек, включая "io" и "package".
Поэтому я могу исключить эти библиотеки из моего интерпретатора, но даже базовая библиотека включает в себя функции "dofile" и "loadfile", которые обращаются к файловой системе.
Как я могу удалить/заблокировать любые небезопасные функции, подобные этим, не вдаваясь с интерпретатором, который даже не имеет базового элемента, такого как функция "ipairs"?
Ответы
Ответ 1
Вы можете установить функциональную среду, в которой вы запускаете ненадежный код, через setfenv(). Здесь схема:
local env = {ipairs}
setfenv(user_script, env)
pcall(user_script)
Функция user_script
может получить доступ только к тому, что находится в ее среде. Таким образом, вы можете в явном виде добавить функции, к которым должен иметь доступ ненадежный код (белый список). В этом случае пользователь script имеет доступ только к ipairs
, но ничего больше (dofile
, loadfile
и т.д.).
См. Lua Sandboxes для примера и дополнительную информацию о песочнице lua.
Ответ 2
Вот решение для 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)
Ответ 3
Живая демонстрация Lua содержит (специализированную) песочницу. Источник доступен свободно.
Ответ 4
Один из самых простых способов устранения нежелательных проблем - сначала загрузить Lua script вашего собственного проекта, который выполняет следующие действия:
load = nil
loadfile = nil
dofile = nil
В качестве альтернативы вы можете использовать setfenv
для создания ограниченной среды, в которую вы можете вставить определенные безопасные функции.
Полностью безопасная песочница немного сложнее. Если вы загружаете код из любого места, имейте в виду, что прекомпилированный код может привести к сбою Lua. Даже полностью ограниченный код может переходить в бесконечный цикл и блокировать бесконечно, если у вас нет системы для его закрытия.
Ответ 5
Вы можете использовать функцию lua_setglobal
, предоставляемую API Lua, чтобы установить эти значения в глобальном пространстве имен на nil
, что эффективно предотвратит доступ к ним любых пользовательских скриптов.
lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "io");
lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "loadfile");
...etc...
Ответ 6
Если вы используете 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
Ответ 7
Вы можете переопределить (отключить) любую функцию Lua, которую вы хотите, а также использовать metatables для большего контроля.