Sand Boxes

lua-users home
wiki

This page uses setfenv in its examples, setfenv is no longer available as of Lua 5.2, so it needs an update. If you use Lua 5.2 do not use this code, I would have updated this myself, but I have no experience in sandboxing.

This page discusses issues relating to sandboxing: running untrusted Lua code in a restricted Lua environment.

WARNING!!!

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.

A Simple Sandbox

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.

Table of Variables

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.

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
isn't safe. For example, pcall(safeloadstring, some_script) will load some_script in global environment. --SergeyRozhenko

--DavidManura

Older Comments

Anonymous: Attacks to consider:

See Also


RecentChanges · preferences
edit · history
Last edited December 12, 2023 6:04 pm GMT (diff)