2009-08-03 21 views
62

So Lua parece ideal para implementar "secuencias de comandos de usuario" seguras dentro de mi aplicación.¿Cómo puedo crear un entorno seguro Lua?

Sin embargo, la mayoría de los ejemplos de incrustación lua parecen incluir la carga de todas las bibliotecas estándar, incluidos "io" y "paquete".

Así que puedo excluir esas librerías de mi intérprete, pero incluso la biblioteca base incluye las funciones "dofile" y "loadfile" que acceden al sistema de archivos.

¿Cómo puedo eliminar/bloquear cualquier función insegura como esta, sin terminar con un intérprete que ni siquiera tiene elementos básicos como la función "ipairs"?

Respuesta

1

Puede usar la función lua_setglobal proporcionada por la API de Lua para establecer esos valores en el espacio de nombres global en nil, lo que evitará efectivamente que los scripts de usuario puedan acceder a ellos.

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

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

...etc... 
+3

desde un punto de vista de la seguridad, nunca confiar en una solución lista negra (i puede olvidar alguna función que es misusable), al menos cuando he listas blancas soluciones (ver algunas de las respuestas más arriba) disponibles . – David

-2

Puede anular (deshabilitar) cualquier función Lua que desee y también se puede utilizar para obtener más metatables de control.

+12

Las metatablas se pueden omitir a través de rawget() y no se deben usar como seguridad, solo por conveniencia. – Amber

+0

gracias por mencionar eso. –

+0

¿No puedes anular el enrutado si quieres? – RCIX

49

Puede establecer el entorno de la función en la que ejecuta el código que no es de confianza en setfenv(). He aquí un resumen:

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

La función user_script sólo puede acceder a lo que hay en su entorno. Entonces, puede agregar explícitamente las funciones a las que desea que tenga acceso el código no confiable (lista blanca). En este caso, la secuencia de comandos del usuario solo tiene acceso a ipairs, pero nada más (dofile, loadfile, etc.).

Consulte Lua Sandboxes para obtener un ejemplo y más información sobre lua sandboxing.

+16

Nota, creo que esto debería ser 'local env = {ipairs = ipairs}'. Y si está ejecutando esto en el lua cli interactivo, envuelva todo en un bucle 'do ... end' para que no pierda los vars locales. – BMitch

+7

Cabe señalar que esta es la forma de hacer Lua 5.1. –

3

Una de las maneras más fáciles de limpiar los indeseables es cargar primero un script Lua de su propia invención, que hace cosas como:

load = nil 
loadfile = nil 
dofile = nil 

Como alternativa, puede utilizar setfenv para crear un entorno restringido que puede insertar funciones seguras específicas en.

Sandboxing totalmente seguro es un poco más difícil. Si carga código desde cualquier lugar, tenga en cuenta que el código precompilado puede bloquear a Lua. Incluso el código completamente restringido puede entrar en un ciclo infinito y bloquearse indefinidamente si no tiene un sistema para apagarlo.

+0

En realidad, no tiene que cargar una secuencia de comandos Lua para aclarar las cosas: puede usar las funciones de API de Lua que mencioné en mi respuesta para eliminar las globales de fuera de Lua. – Amber

+0

De hecho, pero esto es en cierto modo más fácil, de ahí la calificación "más fácil". –

14

La Lua live demo contiene una zona de pruebas (especializada). El source está disponible gratuitamente.

26

Aquí hay una solución para Lua 5.2 (incluyendo un entorno de ejemplo que también funcionaría 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 

después utilizarla, que llamarían su función (my_func) como el siguiente:

pcall_rc, result_or_err_msg = run_sandbox(sandbox_env, my_func, arg1, arg2) 
+1

¿Por qué no usar setfenv? Soy un lua novato, así que tengo curiosidad por saber cuál es la diferencia. –

+7

@Computer Linguist: 'setfenv' ha sido eliminado de 5.2: http://www.lua.org/work/doc/manual.html#8.2 – BMitch

+0

Gracias. Estoy usando 5.1, ya que Plutón no funciona en 5.2. Estoy trabajando en un marco que utiliza la serialización de continuación para permitir que las máquinas/flujos de trabajo de estado de espacio aislado se ejecuten en ajax, y aún así se codifiquen de forma sincrónica. Es para un [juego editado colaborativamente] (http://weaverengine.com). –

0

Si está utilizando Lua 5.1 intente esto:

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 
+4

No puedo comentar la técnica de sandboxing, pero sugiero hacer que las cosas bloqueadas se parezcan más a {os = true, debug = true } entonces es un conjunto, luego la verificación es simplemente si está bloqueado, cosas [clave], y no necesita la función ListaEntrada. – mlepage

Cuestiones relacionadas