构建受限沙箱
创建安全的 _ENV 白名单,并使用 load() 执行不受信任的代码。
构建受限沙箱 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
目标
受限沙箱可以安全地运行不受信任的 Lua 代码:代码可以进行计算、使用字符串和数学函数,但不能访问文件系统、OS 或网络。
白名单方案
从头构建沙箱环境,只加入安全且行为明确的函数。
local function makeSandbox()
return {
print = print,
type = type,
tostring = tostring,
tonumber = tonumber,
pairs = pairs,
ipairs = ipairs,
next = next,
select = select,
unpack = table.unpack,
error = error,
pcall = pcall,
xpcall = xpcall,
math = math,
string = { format=string.format, find=string.find,
match=string.match, gmatch=string.gmatch,
gsub=string.gsub, sub=string.sub,
len=string.len, byte=string.byte, char=string.char },
table = { insert=table.insert, remove=table.remove,
concat=table.concat, sort=table.sort,
unpack=table.unpack, move=table.move },
}
end在沙箱中运行代码
以沙箱作为代码的 _ENV,加载并执行用户代码。
local function runSandboxed(code, env)
local fn, err = load(code, "user", "t", env)
if not fn then return nil, err end
return pcall(fn)
end
local env = makeSandbox()
local ok, err = runSandboxed("return 1+1", env)
print(ok, err) -- true 2捕获输出
将 print 替换为自定义函数,使其追加到缓冲区,而不是写入标准输出。
local output = {}
env.print = function(...)
local parts = {}
for i = 1, select("#", ...) do
parts[i] = tostring(select(i, ...))
end
output[#output+1] = table.concat(parts, "\t")
end返回值
如果要允许沙箱代码返回值,请将编译后的代码块作为函数调用,并捕获其返回值。
local fn = load("return math.sqrt(144)", "user", "t", env)
if fn then
local ok, result = pcall(fn)
print(ok, result) -- true 12.0
end阻止元方法漏洞利用
沙箱代码不应获得对元表或 rawget/rawset 的引用,以免绕过字段限制。
限制字符串长度
请重写字符串操作,以限制输出长度,并防止通过分配超大字符串发动 DoS 攻击。
使用 debug.sethook 限制资源
添加指令计数钩子,以终止失控的代码。
local function sandboxWithLimit(code, env, maxOps)
local ops = 0
debug.sethook(function()
ops = ops + 1
if ops > maxOps then error("Instruction limit exceeded") end
end, "", 100)
local result = table.pack(runSandboxed(code, env))
debug.sethook()
return table.unpack(result, 1, result.n)
end测试沙箱
编写测试,尝试已知的逃逸途径:访问 _G、调用 load、使用 debug,并确认这些尝试都会失败。
已知逃逸途径
常见的逃逸路径包括:string.rep("x",2^30)(内存 DoS)、标准对象上的 ({}).__index、通过字符串元表访问 debug,以及通过 package.loaded 访问 io。
沙箱问题
为什么沙箱不包含 load 或 dofile?
回顾:构建受限沙箱
在自定义 env 表中只加入安全函数。使用 load(code, name, "t", env)。通过 debug.sethook 添加指令计数。测试逃逸途径。绝不能包含 load、dofile、debug 或全局表访问。
常见问题解答
「构建受限沙箱」课时是免费的吗?
是的 — 「构建受限沙箱」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「构建受限沙箱」这节课中我会学到什么?
创建安全的 _ENV 白名单,并使用 load() 执行不受信任的代码。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「构建受限沙箱」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。