lua-users home
lua-l archive

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

Dr. Markus Walther wrote:
I note that both David Given's solution and Doug Roger's improvement
allow violating the integrity of the secure table:

t.newkey = nil

Ah, I didn't realise you were trying to protect against malicious attack; I thought you were just talking about data hiding.

In fact, in this situation, tables basically don't work --- they're fundamentally mutable. If you share the same table to Alice and Bob, then Alice can always mutate the table's state in such a way that Bob will end up with a mutated table. In my approach, Alice can't get at Bob's data directly, but it's still possible for Alice to replace Bob's __index with her own version and eavesdrop.

Your example suffers from the same issue. Take the following code:

t = get_secure_table()
mt = getmetatable(t)
oldindex = mt.__index
mt.__index = function(t, k)
setmetatable(t, mt)

This replaces the *shared* __index with an eavesdropping version that logs all accessed keys.

A more secure approach avoids using shared mutable objects, which basically means no tables.

function create_secure_store()
  local data = {}
  local keys = {}
  local function newkey() local k = {} keys[k] = true end
  local function get(k) if keys[k] then return data[k] end end
  local function put(k, v) if keys[k] then data[k] = v end end

  return newkey, get, put


local newkey, get, put = create_secure_store()
local k = newkey()
put(k, "Hello, world!")

(Finding a way of sharing newkey, get and put between Alice and Bob is left as an exercise to the reader.) Now, all shared state is safely hidden away in upvalues, and the only exposed data are the functions, which aren't mutable, and so can't be suborned.

In fact, that's not strictly true: functions *are* mutable using setfenv(). This means that you need to make sure that your secure functions never try to access the environment, which means they must import all external symbols via upvalues:

-- Dangerous! print can be suborned!
function p(n) print(n) end

-- Safe.
local print = print
function p(n) print(n) end

In general, Lua's not terribly good at this stuff because it lacks the rigour necessary to make sure that you get everything right, all the time --- it's far too easy to make a mistake and expose something, at which point an attacker can fairly easily unravel your whole security system. For example, in your sample code, your exposed object's metatable's __index field points directly at the underlying data store, which means that given a secured table t, this:

data = getmetatable(t).__index

...undoes all the security.

David Given