沙盒 |
|
此页面讨论与沙盒相关的议题:在受限的 Lua 环境中运行不受信任的 Lua 代码。
沙盒很棘手,一般来说很难做好 [3]。您应该从不信任任何东西开始,只允许您绝对确信安全的项目(即白名单而不是黑名单方法)。在没有深入了解语言和实现的情况下,很难确定某些代码是安全的(例如,哈希表性能在极少数情况下可能表现不佳 [4])。Lua 实现中也可能存在错误(参见 [bugs]),因此您应该监控邮件列表上的错误报告,并在发生此类漏洞时可能采取额外的隔离措施,例如操作系统级别的隔离机制(如受限用户帐户或 [chroot 监狱])。您还应该通过防火墙等方式保持操作系统修补和安全。可能还需要操作系统级别的资源限制 [5]。限制 Lua 语言的子集可以缓解其中一些问题。
在 LibrariesAndBindings 的“沙盒”章节中查看一些库。
以下说明可能不完整,仅作为起点。
以下是最简单的沙盒之一。它也是最严格的沙盒之一,除了它不处理资源耗尽问题。
-- 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)
此沙盒中的代码可以在沙盒环境中创建变量,创建原始类型的值(从而分配内存),并执行计算。内存使用和计算没有限制,因此不受信任的代码仍然可能严重影响系统性能,除非进行进一步的限制。沙盒无法访问 I/O 以及其环境之外的函数和变量。沙盒与外部世界通信的唯一方式是影响其环境(例如,获取和设置环境中的变量以及调用该环境中的函数),假设沙盒外部的代码也能够访问这些变量和函数。
您可以编写一个解析器,它接受 Lua 语言的子集(例如,防止循环),但这可能仍然不足以完全防止 CPU 耗尽。
以下是 Lua 5.1 变量列表,以及它们在沙箱环境中使用的安全性描述。请注意,变量是否安全可能**取决于您的特定应用程序的安全要求**和您的 Lua 状态。以下列表不保证完整或正确,仅供参考。要创建沙箱,您应该从一个空环境开始,只引入您确定安全的函数 [1](即白名单而不是黑名单)。您不应该依赖手册提供完整的函数列表(例如 HiddenFeatures)。
注意:以下列表尚未更新到 Lua 5.2。
assert
- 安全
collectgarbage
- 不安全 - 可以全局地、不利地影响垃圾回收
dofile
- 不安全 - 可以读取文件系统上的任意文件。它还可以读取标准输入。代码在全局环境下运行,而不是沙箱,因此这可以用来突破沙箱。相关讨论:DofileNamespaceProposal。如果文件是编译后的字节码,则也不安全(参见 load
)。
error
- 安全
_G
- 不安全 - 默认情况下,它包含全局环境。但是,您可能希望将此变量设置为包含沙箱的环境。
getfenv
- 不安全 - 这可以定位沙箱之外的环境,从而突破沙箱。 LuaList:2007-11/msg00202.html
getmetatable
- 不安全 - 请注意,getmetatable""
返回字符串的元表。修改该元表的内容可能会破坏依赖于此字符串行为的沙箱外部的代码。除非对象通过 __metatable
适当地保护,否则可能存在类似的情况。理想情况下,__metatable
应该是不可变的。
ipairs
-- 安全
load
- 不安全。返回的函数具有全局环境,从而突破了沙箱。更严重的是,如果加载字节码而不是 Lua 源代码,则很危险:LuaList:2010-08/msg00487.html 。
loadfile
- 不安全。参见 load
和 dofile
loadstring
-- 不安全。参见 load
。即使这样
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
pcall(safeloadstring, some_script)
将在全局环境中加载 some_script
。--SergeyRozhenko
next
- 安全
pairs
- 安全
pcall
- 安全
print
- 安全(假设输出到stdout
是可以的)
rawequal
- 不安全(可能) - 绕过元表
rawget
- 不安全 - 绕过元表
rawset
- 不安全 - 绕过元表
select
- 安全
setfenv
- 不安全 - 可以修改调用链中和沙箱外部函数的环境
setmetatable
- 不安全 - 请参阅getmetatable
tonumber
- 安全
tostring
- 安全
type
- 安全
unpack
- 安全
_VERSION
- 安全
xpcall
- 安全
coroutine
- 不安全 - 修改此表可能会影响沙箱外部的代码
coroutine.create
- 安全
coroutine.resume
- 安全
coroutine.running
- 安全
coroutine.status
- 安全
coroutine.wrap
- 安全
coroutine.yield
- 安全(可能) - 假设调用者处理此问题
module
- 不安全 - 例如,修改全局变量(例如package.loaded
)并提供对沙箱外部环境的访问。
require
- 不安全 - 修改全局变量(例如package.loaded
),提供对沙箱外部环境的访问,并访问文件系统。
package
- 不安全 - 修改此表可能会影响沙箱外部的代码
package.*
- 不安全 - 影响沙箱外部的模块加载
package.loaded
- 不安全 - 提供对沙箱外部加载的模块的访问
package.loaders
- 不安全 - 提供对沙箱外部加载模块的访问
package.loadlib
- 不安全 - 加载在 Lua 外部运行的任意可执行代码
package.path/package.cpath
- 不安全(实际上) - 因为这可能只对不安全函数有用。本身,它可能是安全的,用于读取。
package.preload
- 不安全(可能) - 可能会允许加载沙箱外部的模块
package.seeall
- 不安全 - 提供对全局环境的访问
string
- 不安全 - 修改此表可能会影响沙箱外部的代码
string.byte
- 安全
string.char
- 安全
string.dump
- 不安全(可能) - 允许查看函数的实现。
string.find
- 安全 -- 警告:许多类似的函数仍然可能导致 CPU 锁定 [6]
string.format
- 安全
string.gmatch
- 安全
string.gsub
- 安全
string.len
- 安全
string.lower
- 安全
string.match
- 安全
string.rep
- 安全
string.reverse
- 安全
string.sub
- 安全
string.upper
- 安全
table
- 不安全 - 修改此表可能会影响沙箱外部的代码
table.insert
- 安全
table.maxn
- 安全
table.remove
- 安全
table.sort
- 安全
math
- 不安全 - 修改此表可能会影响沙箱外部的代码
math.abs
- 安全
math.acos
- 安全
math.asin
- 安全
math.atan
- 安全
math.atan2
- 安全
math.ceil
- 安全
math.cos
- 安全
math.cosh
- 安全
math.deg
- 安全
math.exp
- 安全
math.floor
- 安全
math.fmod
- 安全
math.frexp
- 安全
math.huge
- 安全
math.ldexp
- 安全
math.log
- 安全
math.log10
- 安全
math.max
- 安全
math.min
- 安全
math.modf
- 安全
math.pi
- 安全
math.pow
- 安全
math.rad
- 安全
math.random
- 安全(大部分) - 但请注意,返回的数字是伪随机的,对该函数的调用会影响后续调用。这可能具有统计学意义。
math.randomseed
- 不安全(可能) - 请参见 math.random
math.sin
- 安全
math.sinh
- 安全
math.sqrt
- 安全
math.tan
- 安全
math.tanh
- 安全
io
- 不安全 - 修改此表可能会影响沙箱外部的代码
io.*
- 这些可能不安全,因为它们提供对文件系统的访问。另请注意,对标准文件句柄(例如 io.stdin
)的 io.close
可能不安全,并可能导致崩溃。
io.read
- 安全(可能)
io.write
- 安全(可能) - 注意:可能消耗所有磁盘空间,从而导致系统崩溃
io.flush
- 安全(可能)
io.type
- 安全
os
- 不安全 - 修改此表可能会影响沙箱外部的代码
os.clock
- 安全
os.date
- 不安全 - 这可能会在某些平台上崩溃(未记录)。例如,os.date'%v'
。据报道,这将在 5.2 或 5.1.3 中修复。
os.difftime
- 安全
os.execute
- 不安全 - 调用外部程序
os.exit
- 不安全 - 终止程序
os.getenv
- 不安全(可能) - 取决于环境变量包含的内容
os.remove
- 不安全 - 修改文件系统
os.rename
- 不安全 - 修改文件系统
os.setlocale
- 不安全 - 修改全局区域设置,影响沙箱外部的代码
os.time
- 安全
os.tmpname
- 不安全(可能) - 仅在于它提供有关文件系统结构的一些信息
debug
- 不安全 - 修改此表可能会影响沙箱外部的代码
debug.*
- 不安全 - 此处的函数可以跳出沙箱并访问沙箱外部的变量。请注意 Lua 参考手册中关于 debug
的警告。
newproxy
- 不安全(可能) - 这是一个未记录的函数(隐藏功能),因此它没有您可以依赖的指定接口。newproxy(nil)
和 newproxy(true)
可能很安全,尽管如果禁用了 getmetatable
,至少对于代理来说,它们相当无用。newproxy(o)
其中 o
是另一个代理对象将为新代理分配 o
的元表,因此对于任何公开的代理 o
,o
或 o
的元表都可能存在副作用。
匿名:要考虑的攻击