How to create a secure Lua sandbox?

So, Lua seems ideal for implementing secure "user scripts" inside my application.

However, most lua implementation examples seem to include loading all the standard libraries, including io and package.

Therefore, I can exclude these libraries from my interpreter, but even the base library includes the "dofile" and "loadfile" functions that access the file system.

How can I remove / block any unsafe functions like these without going into an interpreter that doesn't even have a basic element like the ipairs function?

+63
lua sandbox
Aug 03 '09 at 21:23
source share
7 answers

You can set the functional environment in which you run untrusted code through setfenv (). Here is the diagram:

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

The user_script function can only access what is in its environment. Thus, you can explicitly add functions to which an unreliable code (whitelist) must have access. In this case, the user script has access only to ipairs , but nothing else ( dofile , loadfile , etc.).

See Lua Sandboxes for an example and more information on the lua sandbox.

+50
Aug 03 '09 at 21:37
source share

Here is a solution for Lua 5.2 (including an example environment that will also work in 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 

Then, to use it, you call your function ( my_func ) as follows:

 pcall_rc, result_or_err_msg = run_sandbox(sandbox_env, my_func, arg1, arg2) 
+27
Aug 08 '11 at 12:25
source share

Lua's live demo contains a (specialized) sandbox. The source is available freely.

+15
Aug 04 '09 at 1:31
source share

One of the easiest ways to fix unwanted problems is to first load the Lua script of your own project, which does the following:

 load = nil loadfile = nil dofile = nil 

Alternatively, you can use setfenv to create a limited environment in which you can insert certain safe functions.

A completely secure sandbox is a bit trickier. If you download code from anywhere, keep in mind that precompiled code can cause Lua to crash. Even completely limited code can go into an infinite loop and block indefinitely if you do not have a system to close it.

+4
Aug 04 '09 at 0:22
source share

You can use the lua_setglobal function provided by the Lua API to set these values ​​in the global namespace to nil , which will effectively prevent any user scripts from accessing them.

 lua_pushnil(state_pointer); lua_setglobal(state_pointer, "io"); lua_pushnil(state_pointer); lua_setglobal(state_pointer, "loadfile"); ...etc... 
+2
Aug 03 '09 at 21:33
source share

If you are using Lua 5.1, try the following:

 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
Oct 07 '13 at 3:09
source share

You can override (disable) any Lua function you want, and also use metatables for more control.

-2
Aug 03 '09 at 21:34
source share



All Articles