• entries
455
639
• views
421676

# Simple sandboxing in Lua

91 views

Now, Lua is my scripting language of choice, mostly because it rocks [grin] and because it's the one I've used the most; however until today I hadn't given much consideration into how to sandbox scripts, not until Palish mentioned it on IRC however.

Consider this; you load a script for your game and parse it. Lua executes the code and creates a function of vars, functions and tables in the global enviroment.

Then, during the course of execution you call into this script; these functions can now do ANYTHING. They have access to the IO and OS tables, just for starters, which means they are free to do what they like to the system.

This of course isn't at all safe, thus sandboxing. This allows us to restrict what the functions can do by either disallowing access to them OR only allowing access to certain functions. The latter is safer as it means you don't have to worry about random functions being overlooked and holes appearing.

But, how do we sandbox in Lua? Turns out, it's infact VERY simple to do and all comes down to two factors;
1 - when you load a file you load a chunk which becomes a function.
2 - functions can have their own enviroments.

Often in a program you'll see something like;
dofile("myscriptfile.lua")

This loads 'myscriptfile.lua' in, compiles it, get a function pointer to it and executes that function which places the content into the global enviroment. Broken down the steps become;

f() -- execute the chunk

This is where the fact that functions can have their own enviroments come in handy, you see right now it writes all the changes to the global table (or _G) however by setting a new enviroment to another table, containing only the functions we want it to have access to, we can sandbox the code;

-- only give access to the print functiona = {print = print}f = loadfile("myscriptfile.lua")setfenv(f,a)f()

So, lets say we have the script file as follows;
print "oh hi"function test()	print "oh hi again"	print (type(io))end

When we execute it using the above system we get 'oh hi' printed out to the console. If we then execute the following code;
for k,v in pairs(a) do print(k) end
We'll see that the table 'a' contains our print function and our 'test' function.

If we then call the test function we get the following output;
Quote:
 > a.test()oh hi againtest.txt:5: attempt to call global 'type' (a nil value)stack traceback: test.txt:5: in function 'test' stdin:1: in main chunk [C]: ?>

Note how it blows up on line 5 right where we try to call the undefined 'type' function. Yep, even the functions in the 'a' table are sandboxed within the enviroment defined by 'a'.

I'm working on some ideas of 'enviromental inhertiance' however that is still very much work in progress, who knows, maybe I'll get another article out of it? (or GPG8 here we come? *chuckles*)

There are no comments to display.