Sand Boxes |
|
This page discusses issues relating to sandboxing: running untrusted Lua code in a restricted Lua environment.
Sandboxing is tricky and generally speaking difficult to get right [3]. You should start by trusting nothing and only permit things that you are absolutely sure are safe (i.e. whitelist rather than blacklist approach). It's difficult to be sure some code is safe without thorough knowledge of the language and implementation (e.g. hash table performance may have poor performance in rare cases [4]). It's also possible there could be a bug in the Lua implementation (see [bugs]), so you should monitor bug reports on the mailing list and possibly take additional steps for isolation in the event of such a breach, such as operating system level isolation mechanisms (like restricted user accounts or a [chroot jail]). You should also keep your operating system patched and secured by means like firewalls. Operating system level resource limits may also be necessary [5]. Restricting to a subset of the Lua language can mitigate some of these concerns.
See some libs in "Sandboxing" chapter of LibrariesAndBindings.
The following notes are probably incomplete and intended only as a starting point.
The following is one of the simplest sandboxes. It is also one of the most restrictive, except it doesn't handle resource exhaustion issues.
-- make environment local env = {} -- add functions you know are safe here -- run code under environment [Lua 5.1] local function run(untrusted_code) if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end local untrusted_function, message = loadstring(untrusted_code) if not untrusted_function then return nil, message end setfenv(untrusted_function, env) return pcall(untrusted_function) end -- run code under environment [Lua 5.2] local function run(untrusted_code) local untrusted_function, message = load(untrusted_code, nil, 't', env) if not untrusted_function then return nil, message end return pcall(untrusted_function) end -- test assert(not run [[print(debug.getinfo(1))]]) --> fails assert(run [[x=1]]) --> ok assert(run [[while 1 do end]]) --> ok (but never returns)
Code in this sandbox can create variables in the sandbox environment, create values of primitive types (thereby allocating memory), and perform computations. There is no limit to memory usage and computation, so the untrusted code could still severely impact system performance unless further restrictions are made. The sandbox does not have access to I/O nor functions and variables outside its environment. The only way for the sandbox to communicate with the external world is by affecting its environment (e.g. getting and setting variables and calling functions in that environment), assuming there is code outside the sandbox that also has access to those variables and functions.
You could write a parser that accepts a subset of the Lua language (e.g. prevents loops), but this will likely still be insufficient to fully prevent CPU exhaustion.
The following is a list of Lua 5.1 variables with descriptions of how safe they for use in sandbox environments. Note that whether a variable is safe or not may **depend on the security requirements of your particular application** and your Lua state. No warranty is given that the following listing is complete or correct, but it is only a guideline. To make a sandbox you should start with an empty environment and pull in only functions you know for certain to be safe [1] (i.e. a whitelist not a blacklist). You should not rely on the manual providing a complete list of functions (e.g. HiddenFeatures).
NOTE: The following list has not been updated for Lua 5.2.
assert
- SAFE
collectgarbage
- UNSAFE - can globally and adversely affect garbage collection
dofile
- UNSAFE - The may read arbitrary files on the file system. It can also read standard input. The code is run under the global environment, not the sandbox, so this can be used to break out of the sandbox. Related discussion: DofileNamespaceProposal. Also unsafe if file is compiled bytecodes (see load
).
error
- SAFE
_G
- UNSAFE - by default this contains the global environment. You may, however, want to set this variable to contain the environment of the sandbox.
getfenv
- UNSAFE - this can locate an environment outside of the sandbox, thereby breaking out of the sandbox. LuaList:2007-11/msg00202.html
getmetatable
- UNSAFE - Note that getmetatable""
returns the metatable of strings. Modification of the contents of that metatable can break code outside the sandbox that relies on this string behavior. Similar cases may exist unless objects are protected appropriately via __metatable
. Ideally __metatable
should be immutable.
ipairs
-- SAFE
load
- UNSAFE. The function returned has the global environment, thereby breaking out of the sandbox. More seriously, is dangerous if loading bytecode rather than Lua source: LuaList:2010-08/msg00487.html .
loadfile
- UNSAFE. See load
and dofile
loadstring
-- UNSAFE. See load
. Even this:
local oldloadstring = loadstring local function safeloadstring(s, chunkname) local f, message = oldloadstring(s, chunkname) if not f then return f, message end setfenv(f, getfenv(2)) return f end
pcall(safeloadstring, some_script)
will load some_script
in global environment. --SergeyRozhenko
next
- SAFE
pairs
- SAFE
pcall
- SAFE
print
- SAFE (assuming output to stdout
is ok)
rawequal
- UNSAFE (potentially?) - bypasses metatables
rawget
- UNSAFE - bypasses metatables
rawset
- UNSAFE - bypasses metatables
select
- SAFE
setfenv
- UNSAFE - can modify environments of functions that are up the call-chain and outside the sandbox
setmetatable
- UNSAFE - see getmetatable
tonumber
- SAFE
tostring
- SAFE
type
- SAFE
unpack
- SAFE
_VERSION
- SAFE
xpcall
- SAFE
coroutine
- UNSAFE - modifying this table could affect code outside the sandbox
coroutine.create
- SAFE
coroutine.resume
- SAFE
coroutine.running
- SAFE
coroutine.status
- SAFE
coroutine.wrap
- SAFE
coroutine.yield
- SAFE (probably) - assuming caller handles this
module
- UNSAFE - for example, modifies globals (e..g package.loaded
) and provides access to environments outside the sandbox.
require
- UNSAFE - modifies globals (e.g. package.loaded
), provides access to environments outside the sandbox, and accesses the file system.
package
- UNSAFE - modifying this table could affect code outside the sandbox
package.*
- UNSAFE - affects module loading outside the sandbox
package.loaded
- UNSAFE - provides access to modules loaded outside the sandbox
package.loaders
- UNSAFE - provides access to loading modules outside the sandbox
package.loadlib
- UNSAFE - loads arbitrary executable code that runs outside of Lua
package.path/package.cpath
- UNSAFE (effectively) since this is probably useful only for UNSAFE functions. In itself, it is probably SAFE, for reading that is.
package.preload
- UNSAFE (probably) - may allow loading modules outside the sandbox
package.seeall
- UNSAFE - provides access to the global environment
string
- UNSAFE - modifying this table could affect code outside the sandbox
string.byte
- SAFE
string.char
- SAFE
string.dump
- UNSAFE (potentially) - allows seeing implementation of functions.
string.find
- SAFE -- warning: a number of functions like this can still lock up the CPU [6]
string.format
- SAFE
string.gmatch
- SAFE
string.gsub
- SAFE
string.len
- SAFE
string.lower
- SAFE
string.match
- SAFE
string.rep
- SAFE
string.reverse
- SAFE
string.sub
- SAFE
string.upper
- SAFE
table
- UNSAFE - modifying this table could affect code outside the sandbox
table.insert
- SAFE
table.maxn
- SAFE
table.remove
- SAFE
table.sort
- SAFE
math
- UNSAFE - modifying this table could affect code outside the sandbox
math.abs
- SAFE
math.acos
- SAFE
math.asin
- SAFE
math.atan
- SAFE
math.atan2
- SAFE
math.ceil
- SAFE
math.cos
- SAFE
math.cosh
- SAFE
math.deg
- SAFE
math.exp
- SAFE
math.floor
- SAFE
math.fmod
- SAFE
math.frexp
- SAFE
math.huge
- SAFE
math.ldexp
- SAFE
math.log
- SAFE
math.log10
- SAFE
math.max
- SAFE
math.min
- SAFE
math.modf
- SAFE
math.pi
- SAFE
math.pow
- SAFE
math.rad
- SAFE
math.random
- SAFE (mostly) - but note that returned numbers are pseudorandom, and calls to this function affect subsequent calls. This may have statistical implications.
math.randomseed
- UNSAFE (maybe) - see math.random
math.sin
- SAFE
math.sinh
- SAFE
math.sqrt
- SAFE
math.tan
- SAFE
math.tanh
- SAFE
io
- UNSAFE - modifying this table could affect code outside the sandbox
io.*
- These can be UNSAFE as they provide access to the file system. Note also that io.close
on standard file handles (e.g. io.stdin
) may be unsafe and could cause a crash.
io.read
- SAFE (probably)
io.write
- SAFE (probably) - note: potentially could consume all disk space, thereby bringing down a system
io.flush
- SAFE (probably)
io.type
- SAFE
os
- UNSAFE - modifying this table could affect code outside the sandbox
os.clock
- SAFE
os.date
- UNSAFE - This can crash on some platforms (undocumented). For example, os.date'%v'
. It is reported that this will be fixed in 5.2 or 5.1.3.
os.difftime
- SAFE
os.execute
- UNSAFE - calls external programs
os.exit
- UNSAFE - terminates program
os.getenv
- UNSAFE (potentially) - depending on what environment variables contain
os.remove
- UNSAFE - modifies file system
os.rename
- UNSAFE - modifies file system
os.setlocale
- UNSAFE - modifies global locale, affecting code outside the sandbox
os.time
- SAFE
os.tmpname
- UNSAFE (maybe) - only in that it provides some information on the structure of the file system
debug
- UNSAFE - modifying this table could affect code outside the sandbox
debug.*
- UNSAFE - functions here can break out of the sandbox and access variables outside the sandbox. Note warnings in the Lua Reference Manual concerning debug
.
newproxy
- UNSAFE (potentially) - This is an undocumented function (HiddenFeatures), so it does not have a specified interface that you can rely on. newproxy(nil)
and newproxy(true)
are probably safe, though fairly useless if getmetatable
is disabled, at least for the proxy. newproxy(o)
where o
is another proxy object will assign the metatable of o
to the new proxy, so there are potential side-effects on o
or the metatable of o
for any exposed proxy o
.
Anonymous: Attacks to consider: