lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

I'm currently working on a similar trick in my project, though I don't
copy the whole of _G for efficiency reasons, if nothing else. Instead,
I create a clean environment, and give it a metatable that makes it
pull values from the global environment if they're not present in the

Couple of important notes: the global environment and the tables it
contains are 'read only' in my setup. This is because the global
environment is expected to persist for long periods of time (months),
and is shared by multiple scripts. I don't want any script polluting
the global namespace and potentially interefering with another script.

I also use the '__usedindex' patch to prevent certain things being
altered, such as the __environment and __parent magic values. I also
use more magic to stop people using rawset/rawget and
getmetatable/setmetatable on secured tables, so this is only part of
the whole mechanism.

This is all a bit quick'n'dirty, to be honest, and I fully expect to
replace it with something a little nicer as and when it comes to mind
;-) The line where data is written back to the parent environment
should really deep-copy data to the temporary environment instead, for

f is the function to sandbox, by the way.

  local eproxy = {}
  eproxy.__parent = getfenv(2)
  eproxy.__index = function(t, n)
    local env = rawget(t, "__environment")
    local parent = rawget(getmetatable(t), "__parent")
    if env and env[n] then return env[n] end

    if !parent then return nil end
    return parent[n]
  eproxy.__newindex = function(t, n, v)
    local env = rawget(t, "__environment")
    local parent = rawget(gmget(t), "__parent")

    if !env then
      rawset(t, "__environment", {})
      env = rawget(t, "__environment")

    if rawget(parent,n) then parent[n] = v end


  local env = {}

On Thu, Jul 17, 2008 at 9:44 AM, Michael Gerbracht <> wrote:
> I am writing a program where an external user script is loaded and
> executed. Therefore it is AFAIK good practice to implement a sandbox which
> restricts the user to use only a certain set of functions. Still I would
> like to enable the user to use most of Lua's functions and restrict only
> those functions which would enable the user to break the sandbox and e.g.
> access _G.
> E.g. I found out that it is not enough to call loadfile("external_file")
> with a restricted environment because if external_file loads a second
> external file this has access to the global environment again. What I did
> was to implement some code like this:
> function envloadfile(file)  -- secure loadfile
>  local x
>  x = loadfile(file)
>  setfenv(x, env)
>  return x
> end
> env = {loadfile=envloadfile}  -- see comment
> local f,ret,err
> f = envloadfile("external_file")
> ret,err = pcall(f)
> I know that the env environment is very restrictive and is there for test
> purposes only. But as far as I can tell it works (second external file is
> in restricted environment).
> My first question is: Is the above code ok, or would you implement it in
> another way?
> My second question is: What other functions need to be either blocked or
> restricted so that the user has no chance to break the sandbox? For
> example, do I need to block the setfenv function?
> The third question is: Instead of defining which functions are allowed one
> by one, I would rather like to copy an existing environment (_G) and
> change it. I tried this with the following code:
> function table.copy(data)  -- taken from book "Lua Programming", p149
>  local t = {}
>  for Key, Val in pairs(data) do
>    Key = type(Key) == "table" and table.copy(Key) or Key
>    Val = type(Val) == "table" and table.copy(Val) or Val
>    t[Key] = Val
>  end
>  return t
> end
> env = table.copy(_G)
> env.loadfile = envloadfile
> The problem is that the table.copy function runs infinitely. I guess this
> is because _G refers to itself. But I do not know how to avoid that.
> Thank you very much,
> Michael
> --
> ...using RiscLua 4.14, built from Lua 5.1.3