Wie kann ich eine sichere Lua-Sandbox erstellen?


74

Daher scheint Lua ideal für die Implementierung sicherer "Benutzerskripte" in meiner Anwendung zu sein.

Die meisten Beispiele für das Einbetten von Lua scheinen jedoch das Laden aller Standardbibliotheken zu umfassen, einschließlich "io" und "package".

Ich kann diese Bibliotheken also von meinem Interpreter ausschließen, aber selbst die Basisbibliothek enthält die Funktionen "dofile" und "loadfile", die auf das Dateisystem zugreifen.

Wie kann ich unsichere Funktionen wie diese entfernen / blockieren, ohne nur einen Interpreter zu haben, der nicht einmal grundlegende Dinge wie die "ipairs" -Funktion enthält?

Antworten:


53

Sie können die Funktionsumgebung festlegen, in der Sie den nicht vertrauenswürdigen Code über setfenv () ausführen . Hier ist eine Übersicht:

local env = {ipairs}
setfenv(user_script, env)
pcall(user_script)

Die user_scriptFunktion kann nur auf das zugreifen, was sich in ihrer Umgebung befindet. Sie können dann explizit die Funktionen hinzufügen, auf die der nicht vertrauenswürdige Code Zugriff haben soll (Whitelist). In diesem Fall hat der Benutzer Skript nur den Zugriff auf , ipairsaber sonst nichts ( dofile, loadfileusw.).

Ein Beispiel und weitere Informationen zum Lua-Sandboxing finden Sie unter Lua-Sandkästen .


18
Beachten Sie, ich glaube, das sollte sein local env = {ipairs=ipairs}. Und wenn Sie dies auf dem interaktiven lua cli ausführen, wickeln Sie das Ganze in eine do ... endSchleife, damit Sie die lokalen Variablen nicht verlieren.
BMitch

9
Es sollte beachtet werden, dass dies die Lua 5.1-Methode ist, um Dinge zu tun.
John K

In Lua 5.2 und höher würden Sie stattdessen load()das envArgument verwenden, was setfenv()ohnehin eine viel sicherere Alternative ist, da Sie es nicht so leicht vergessen können.
DarkWiiPlayer

34

Hier ist eine Lösung für Lua 5.2 (einschließlich einer Beispielumgebung, die auch in 5.1 funktionieren würde):

-- save a pointer to globals that would be unreachable in sandbox
local e=_ENV

-- sample sandbox environment
sandbox_env = {
  ipairs = ipairs,
  next = next,
  pairs = pairs,
  pcall = pcall,
  tonumber = tonumber,
  tostring = tostring,
  type = type,
  unpack = unpack,
  coroutine = { create = coroutine.create, resume = coroutine.resume, 
      running = coroutine.running, status = coroutine.status, 
      wrap = coroutine.wrap },
  string = { byte = string.byte, char = string.char, find = string.find, 
      format = string.format, gmatch = string.gmatch, gsub = string.gsub, 
      len = string.len, lower = string.lower, match = string.match, 
      rep = string.rep, reverse = string.reverse, sub = string.sub, 
      upper = string.upper },
  table = { insert = table.insert, maxn = table.maxn, remove = table.remove, 
      sort = table.sort },
  math = { abs = math.abs, acos = math.acos, asin = math.asin, 
      atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos, 
      cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor, 
      fmod = math.fmod, frexp = math.frexp, huge = math.huge, 
      ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max, 
      min = math.min, modf = math.modf, pi = math.pi, pow = math.pow, 
      rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh, 
      sqrt = math.sqrt, tan = math.tan, tanh = math.tanh },
  os = { clock = os.clock, difftime = os.difftime, time = os.time },
}

function run_sandbox(sb_env, sb_func, ...)
  local sb_orig_env=_ENV
  if (not sb_func) then return nil end
  _ENV=sb_env
  local sb_ret={e.pcall(sb_func, ...)}
  _ENV=sb_orig_env
  return e.table.unpack(sb_ret)
end

Um es dann zu verwenden, würden Sie Ihre Funktion ( my_func) wie folgt aufrufen :

pcall_rc, result_or_err_msg = run_sandbox(sandbox_env, my_func, arg1, arg2)

1
Warum nicht setfenv verwenden? Ich bin ein Lua-Neuling, also bin ich neugierig, was der Unterschied ist.
Lilith River

9
@ Computer Linguist: setfenvwurde aus 5.2 entfernt: lua.org/work/doc/manual.html#8.2
BMitch

1
Ich versuche, Ihren Code auszuführen, aber ich verstehe nicht, wo Sie my_func deklarieren würden und wie, damit dies funktioniert. Es bellt, dass "42: Versuch, den Aufwert 'e' (ein Nullwert) zu indizieren"
AlfredoVR

1
Das funktioniert nicht. Funktionen verwenden die _ENV, mit der sie kompiliert wurden, nicht die _ENV, von der sie aufgerufen wurden. Sie müssen debug.setupvalue (sb_func, 1, sb_env) aufrufen, um _ENV zu ersetzen, bevor Sie es aufrufen.
John K

1
Das Austauschen von _ENV nach dem Laden einer Funktion ist die 5.1-Methode. In 5.2 übergeben Sie einfach sb_env als ENV-Parameter an "load", d. H. load ("function sb_func () return nil end", "", "t", sb_env) dann können Sie sb_func jedes Mal wie eine reguläre Funktion aufrufen.
John K


5

Eine der einfachsten Möglichkeiten, unerwünschte Ereignisse zu beseitigen, besteht darin, zuerst ein Lua-Skript zu laden, das Sie selbst erstellt haben.

load = nil
loadfile = nil
dofile = nil

Alternativ können Sie setfenveine eingeschränkte Umgebung erstellen, in die Sie bestimmte sichere Funktionen einfügen können.

Völlig sicheres Sandboxen ist etwas schwieriger. Wenn Sie Code von überall laden, beachten Sie, dass vorkompilierter Code Lua zum Absturz bringen kann. Selbst vollständig eingeschränkter Code kann in eine Endlosschleife gelangen und unbegrenzt blockieren, wenn Sie kein System zum Herunterfahren haben.


Sie müssen kein Lua-Skript laden, um keine Probleme zu lösen. Sie können die Lua-API-Funktionen verwenden, die ich in meiner Antwort erwähnt habe, um die globalen Elemente von außerhalb von Lua zu löschen.
Amber

In der Tat, aber dies ist in gewisser Weise einfacher, daher die "einfachste" Qualifikation.
John Calsbeek

3

Mit der lua_setglobalvon der Lua-API bereitgestellten Funktion können Sie die Werte im globalen Namespace festlegen, auf nildie effektiv verhindert wird, dass Benutzerskripte auf sie zugreifen können.

lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "io");

lua_pushnil(state_pointer);
lua_setglobal(state_pointer, "loadfile");

...etc...

7
Aus Sicherheitsgründen würde ich niemals einer Blacklisting-Lösung vertrauen (ich vergesse möglicherweise nur eine Funktion, die missbraucht werden kann), zumindest wenn ich Whitelisting-Lösungen (siehe einige der obigen Antworten) zur Verfügung habe.
David

1

Wenn Sie Lua 5.1 verwenden, versuchen Sie Folgendes:

blockedThings = {'os', 'debug', 'loadstring', 'loadfile', 'setfenv', 'getfenv'}
scriptName = "user_script.lua"

function InList(list, val) 
    for i=1, #list do if list[i] == val then 
        return true 
    end 
end

local f, msg = loadfile(scriptName)

local env = {}
local envMT = {}
local blockedStorageOverride = {}
envMT.__index = function(tab, key)
    if InList(blockedThings, key) then return blockedStorageOverride[key] end
    return rawget(tab, key) or getfenv(0)[key]
end
envMT.__newindex = function(tab, key, val)
    if InList(blockedThings, key) then
        blockedStorageOverride[key] = val
    else
        rawset(tab, key, val)
    end
end

if not f then
    print("ERROR: " .. msg)
else
    setfenv(f, env)
    local a, b = pcall(f)
    if not a then print("ERROR: " .. b) end
end

4
Ich kann die Sandbox-Technik nicht kommentieren, aber ich würde vorschlagen, blockierte Dinge eher wie {os = true, debug = true} aussehen zu lassen. Es handelt sich also um eine Menge. Dann wird einfach geprüft, ob blockierte Dinge [Schlüssel] vorhanden sind, und Sie tun dies nicht benötigen die InList-Funktion.
Mlepage

-2

Sie können jede gewünschte Lua-Funktion überschreiben (deaktivieren) und Metatabellen für mehr Kontrolle verwenden .


14
Metatables können über rawget () umgangen werden und sollten nicht aus Sicherheitsgründen, sondern nur zur Vereinfachung verwendet werden.
Amber

Können Sie Rawget nicht überschreiben, wenn Sie möchten?
RCIX

RCIX, du kannst es schaffen. Sie haben die Freiheit , fast alles in Lua zu tun :-)
Nick Dandoulakis

1
Sie könnten Rawget überschreiben, aber dies würde auch die nicht böswillige metatierbare Funktionalität beeinträchtigen und ist keine ideale Lösung.
Amber
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.