沙盒 |
|
此頁面會針對沙盒議題進行討論:在受限的 Lua 環境中執行不可信任的 Lua 程式碼。
沙盒很棘手,而且一般來說很難做對 [3]。您一開始不應該信任任何事物,只允許您絕對確定安全的事物(例如白名單而非黑名單的做法)。如果對於語言和實作沒有深入了解,便很難確保某段程式碼是安全的(例如哈希表效能可能在少數狀況下出現不佳效能 [4])。Lua 實作中也可能存在錯誤(請參閱 [bugs]),因此您應該監控郵件清單中的錯誤報告,若發生這類漏洞,可能需要採取進一步的隔離措施,例如作業系統等級的隔離機制(例如受限使用者帳戶或 [chroot 監獄])。您也應該保持作業系統的修補程式更新,並透過防火牆等方式強化安全性。 作業系統等級的資源限制可能也有必要 [5]。限制使用 Lua 語言的子集可以緩解其中的部分疑慮。
請參閱 LibrariesAndBindings 的「Sandboxing」章節中的部分函式庫。
以下註解可能不完整,且僅供作起點。
以下是最簡單的沙盒之一。它也是其中限制最多的沙盒,除了不處理資源耗盡問題之外。
-- 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.htmlgetmetatable
- 不安全 - 注意 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
。 --SergeyRozhenkonext
- 安全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.close
(例如 io.stdin
)可能不安全,並可能導致系統崩潰。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
- 不安全(可能) - 這是一個未記載的功能(HiddenFeatures),因此它沒有您可以依賴的指定介面。newproxy(nil)
和 newproxy(true)
可能安全,不過如果停用 getmetatable
幾乎無用,至少對 proxy 無用。newproxy(o)
,其中 o
是另一個 proxy 物件,會將 o
的 metatable 指定給新 proxy,因此,對於任何公開 proxy o
,可能會對 o
或 o
的 metatable 造成副作用。
匿名者:應考量的攻擊