Lua semble donc idéal pour implémenter des "scripts utilisateur" sécurisés dans mon application.
Cependant, la plupart des exemples d'intégration de lua semblent inclure le chargement de toutes les bibliothèques standard, y compris "io" et "package".
Je peux donc exclure ces bibliothèques de mon interprète, mais même la bibliothèque de base inclut les fonctions "dofile" et "loadfile" qui accèdent au système de fichiers.
Comment puis-je supprimer/bloquer des fonctions dangereuses comme celles-ci, sans se retrouver avec un interprète qui n'a même pas de trucs de base comme la fonction "ipairs"?
Vous pouvez définir l'environnement de fonction dans lequel vous exécutez le code non approuvé via setfenv (). Voici un aperçu:
local env = {ipairs}
setfenv(user_script, env)
pcall(user_script)
Le user_script
la fonction ne peut accéder qu'à ce qui se trouve dans son environnement. Ainsi, vous pouvez ensuite ajouter explicitement les fonctions auxquelles vous souhaitez que le code non approuvé ait accès (liste blanche). Dans ce cas, le script utilisateur n'a accès qu'à ipairs
mais à rien d'autre (dofile
, loadfile
, etc.).
Voir Lua Sandboxes pour un exemple et plus d'informations sur le lua sandboxing.
Voici une solution pour Lua 5.2 (incluant un exemple d'environnement qui fonctionnerait également en 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
Ensuite, pour l'utiliser, vous appelleriez votre fonction (my_func
) comme suit:
pcall_rc, result_or_err_msg = run_sandbox(sandbox_env, my_func, arg1, arg2)
Lua live demo contient un sandbox (spécialisé). Le source est disponible gratuitement.
L'un des moyens les plus simples d'éliminer les indésirables est de charger d'abord un script Lua de votre propre conception, qui fait des choses comme:
load = nil
loadfile = nil
dofile = nil
Alternativement, vous pouvez utiliser setfenv
pour créer un environnement restreint dans lequel vous pouvez insérer des fonctions sûres spécifiques.
Le sandbox totalement sûr est un peu plus difficile. Si vous chargez du code de n'importe où, sachez que le code précompilé peut planter Lua. Même du code complètement restreint peut entrer dans une boucle infinie et se bloquer indéfiniment si vous n'avez pas de système pour l'arrêter.
Vous pouvez utiliser le lua_setglobal
fonction fournie par l'API Lua pour définir ces valeurs dans l'espace de noms global sur nil
, ce qui empêchera efficacement tout script utilisateur d'y accéder.
lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "io");
lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "loadfile");
...etc...
Si vous utilisez Lua 5.1, essayez ceci:
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