Python中不安全的反序列化操作示例
1.Python 的 pickling 和 unpickling?
pickling 和 unpickling 是指序列化和反序列化對象的過程。具體來說,pickling 是將一個 Python 對象轉(zhuǎn)換為一個字節(jié)流,以便可以將其存儲到文件中或通過網(wǎng)絡(luò)傳輸;而 unpickling 是將字節(jié)流重新轉(zhuǎn)換回原來的 Python 對象。
1)Pickling 示例操作: 為了實現(xiàn) pickling,我們通常會使用 pickle 模塊。例如:
import pickle
data = {"name": "Alice", "age": 25, "is_student": True}
# 將對象序列化并寫入文件
with open("data.pickle", "wb") as f:
pickle.dump(data, f)2)Unpickling 示例操作: 對應(yīng)地,要進(jìn)行 unpickling,可以這樣操作:
import pickle
# 從文件中讀取字節(jié)流并反序列化為對象
with open("data.pickle", "rb") as f:
data = pickle.load(f)
print(data) # 輸出:{'name': 'Alice', 'age': 25, 'is_student': True}3)安全性問題: 值得注意的是,使用 pickle 時要小心安全問題。因為 pickle 可以執(zhí)行任意代碼,所以從不可信來源加載 pickle 數(shù)據(jù)是危險的,可能導(dǎo)致代碼執(zhí)行漏洞。因此,盡量避免加載未經(jīng)驗證的 pickle 文件。
4)絕對 file path 和相對文件存取: 在實際應(yīng)用中,我們可能會使用相對路徑來讀取或?qū)懭?pickle 文件,以增強(qiáng)代碼的可移植性。例如:data.pickle 相較于絕對路徑 "/path/to/data.pickle" 更方便在不同環(huán)境下運行。
5)pickle 模塊的替代品: 如果需要更安全的序列化方法,可以考慮使用 json 模塊,它只支持基本數(shù)據(jù)類型的序列化和反序列化,并不執(zhí)行任何代碼。因此,它是更安全的選擇,盡管功能上沒有 pickle 強(qiáng)大。
import json
data = {"name": "Alice", "age": 25, "is_student": True}
# 序列化
with open("data.json", "w") as f:
json.dump(data, f)
# 反序列化
with open("data.json", "r") as f:
data = json.load(f)
print(data) # 輸出:{'name': 'Alice', 'age': 25, 'is_student': True}2.如何利用pickle模塊的反序列化來執(zhí)行惡意代碼?
簡單來說,pickle 之所以不安全,是因為它不僅僅是在存儲數(shù)據(jù),它還在存儲**“如何構(gòu)建對象”的指令**。
1. 為什么 pickle 是不安全的?
pickle 的設(shè)計目標(biāo)是序列化幾乎任何 Python 對象。為了實現(xiàn)這一點,它使用了一種名為 Stack Machine(棧機(jī)) 的機(jī)制。
當(dāng)你“反序列化”(unpickling)一個文件時,你實際上是在運行一段 Pickle 虛擬機(jī)代碼。pickle 模塊會盲目地執(zhí)行這些指令來重建對象。如果指令說“去運行這個系統(tǒng)命令”,Python 就會照辦。
核心風(fēng)險點:
pickle.load()并沒有任何沙箱保護(hù)。它信任它讀取的所有數(shù)據(jù)。
2. 為什么可以執(zhí)行任意代碼?
這主要歸功于 Python 對象協(xié)議中的一個特殊魔術(shù)方法:__reduce__。
當(dāng) pickle 嘗試序列化一個對象時,它會檢查該對象是否有 __reduce__ 方法。如果有,該方法必須返回一個元組。這個元組最常見的情況包含兩個元素:
- 一個可調(diào)用對象(函數(shù))。
- 一個參數(shù)元組,供上述函數(shù)調(diào)用。
在反序列化時,pickle 會自動調(diào)用這個函數(shù)并傳入?yún)?shù)。這就是攻擊點。
3. Python 例子演示
讓我們通過代碼來看看如何構(gòu)建一個“惡意”的 pickle 包。
下面的代碼創(chuàng)建了一個類,它的 __reduce__ 方法指示 pickle 在解封時運行系統(tǒng)命令(例如在 Windows 上打開計算器,或在 Linux/Mac 上創(chuàng)建一個文件)。
import pickle
import os
class MaliciousPickle:
def __reduce__(self):
# os.system 是我們要調(diào)用的函數(shù)
# ('calc',) 是傳給它的參數(shù)(Windows 下打開計算器)
# 如果是 Linux/Mac,可以改為 ('touch /tmp/hacked.txt',)
return (os.system, ('calc',))
# 1. 序列化:黑客生成惡意的 pickle 數(shù)據(jù)
evil_data = pickle.dumps(MaliciousPickle())
# 2. 假設(shè)我們將 evil_data 發(fā)送給了受害者
# 3. 反序列化:受害者僅僅是想“讀取數(shù)據(jù)”
print("正在解封數(shù)據(jù)...")
pickle.loads(evil_data) # 此時,計算器程序會被彈出!在上面的例子中,pickle.loads() 本意是恢復(fù)一個對象,但它卻執(zhí)行了 os.system('calc')。
- 如果黑客把命令換成
rm -rf /(刪除系統(tǒng)文件)。 - 或者換成一個反彈 Shell(Reverse Shell),遠(yuǎn)程控制你的服務(wù)器。
- 你的程序甚至不需要以管理員權(quán)限運行,只要它有權(quán)限執(zhí)行的操作,黑客都能做。
4. 如何防范?
- 永遠(yuǎn)不要
pickle.load()來自不信任來源的數(shù)據(jù)。 這是金科玉律。 - 使用更安全的格式: 優(yōu)先選擇
JSON、XML或MessagePack。這些格式只存儲純數(shù)據(jù),不包含執(zhí)行邏輯。 - 簽名驗證: 如果必須使用
pickle,請使用hmac對數(shù)據(jù)進(jìn)行簽名,確保數(shù)據(jù)在傳輸過程中沒有被篡改。
pickle 是為了方便和靈活而設(shè)計的,但這種靈活性賦予了數(shù)據(jù)“生命”,讓它在被加載時能執(zhí)行動作。在安全領(lǐng)域,**“數(shù)據(jù)與指令混淆”**通常是漏洞的根源。
3.Json模塊的反序列化為啥是安全的?
json 模塊之所以被認(rèn)為是安全的,核心原因在于它的本質(zhì)屬性和解析機(jī)制與 pickle 完全不同。
我們可以從以下三個維度來拆解為什么 JSON 不會像 pickle 那樣“反客為主”:
1. 數(shù)據(jù) vs 指令 (Data vs. Code)
這是最根本的區(qū)別。
- Pickle 是一種“腳本”:
pickle序列化的結(jié)果實際上是一串給 Pickle 虛擬機(jī)運行的指令。它包含“創(chuàng)建對象”、“查找模塊”、“調(diào)用函數(shù)”等操作。正如我們之前看到的,__reduce__就像是在數(shù)據(jù)包里藏了一張“指令單”。 - JSON 是“純描述”: JSON(JavaScript Object Notation)是一種聲明式的數(shù)據(jù)交換格式。它只能表示有限的幾種數(shù)據(jù)類型:字符串、數(shù)字、布爾值、null、數(shù)組(列表)和對象(字典)。
打個比方:
- Pickle 就像是你收到一個包裹,里面有一張紙條寫著:“去廚房打開煤氣灶”。如果你照做了,就會發(fā)生動作。
- JSON 就像是你收到一張清單,上面寫著:“蘋果:5個,香蕉:3個”。這張紙條本身沒有任何權(quán)力讓你去做動作,它只是在描述狀態(tài)。
2. 解析器的局限性
json.loads() 的工作原理是極其受限的。
當(dāng)你運行 json.loads(data) 時,Python 的 JSON 解析器只會在內(nèi)存中構(gòu)建基礎(chǔ)的內(nèi)置類型(如 dict, list, str)。它完全不知道什么是 Python 類,也沒有機(jī)制去觸發(fā) os.system() 這樣的外部系統(tǒng)調(diào)用。
在解析 JSON 時,即便你在 JSON 字符串里寫了類似代碼的內(nèi)容:
{
"command": "os.system('rm -rf /')"
}解析器也只會把它當(dāng)作一個普通的字符串。除非你自己在代碼里顯式地拿到這個字符串并傳給 eval() 執(zhí)行,否則它就只是一串死板的字符。
3. 缺乏“對象重建”機(jī)制
pickle 之所以危險,是因為它支持自動重建自定義對象。
- 在
pickle中,如果數(shù)據(jù)說它是MyClass類型,pickle會嘗試導(dǎo)入這個類并實例化它。 - 在
json中,沒有任何語法可以指定“這是一個某個類的實例”。JSON 只知道[]是數(shù)組,{}是鍵值對。
比較表
| 特性 | Pickle | JSON |
| 本質(zhì) | 二進(jìn)制指令流(可執(zhí)行) | 文本字符串(純數(shù)據(jù)) |
| 支持類型 | 幾乎所有 Python 對象 | 僅基礎(chǔ)類型 (str, int, dict, list等) |
| 自定義類 | 自動觸發(fā) __init__ 或 __reduce__ | 不支持,除非手動編寫解析邏輯 |
| 安全性 | 不安全 (信任數(shù)據(jù)即信任代碼) | 安全 (解析過程不涉及邏輯執(zhí)行) |
4. 潛在的例外(安全警告)
雖然 json.loads() 本身是安全的,但作為開發(fā)者,如果處理不當(dāng)仍會引入風(fēng)險:
- 后期處理漏洞: 如果你把解析出來的 JSON 數(shù)據(jù)直接傳給了
eval()、exec()或者數(shù)據(jù)庫查詢語句(SQL 注入),那么危險依然存在。 - 拒絕服務(wù)攻擊 (DoS): 極度深層嵌套的 JSON(例如
[[[[...]]]])可能會消耗大量的 CPU 或內(nèi)存資源,導(dǎo)致程序崩潰,但這屬于穩(wěn)定性問題,而非任意代碼執(zhí)行。
總結(jié)
JSON 的安全性源于它的簡單性和被動性。它只是一面反映數(shù)據(jù)的鏡子,而 pickle 卻是一臺藏在盒子里的微型計算機(jī)。
既然我們談到了安全的數(shù)據(jù)交換,你想了解一下如何為你的 JSON 數(shù)據(jù)添加“數(shù)字簽名”,以確保它在傳輸過程中沒有被第三方篡改嗎?
參考文檔
https://docs.python.org/zh-cn/3/library/pickle.html
https://www.runoob.com/python3/python-pickle.html
https://www.datacamp.com/tutorial/pickle-python-tutorial
到此這篇關(guān)于Python中不安全的反序列化操作示例的文章就介紹到這了,更多相關(guān)Python不安全反序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3實現(xiàn)飛機(jī)大戰(zhàn)游戲
這篇文章主要為大家詳細(xì)介紹了Python3實現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-04-04
Keras構(gòu)建神經(jīng)網(wǎng)絡(luò)踩坑(解決model.predict預(yù)測值全為0.0的問題)
這篇文章主要介紹了Keras構(gòu)建神經(jīng)網(wǎng)絡(luò)踩坑(解決model.predict預(yù)測值全為0.0的問題),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Python實現(xiàn)在matplotlib中兩個坐標(biāo)軸之間畫一條直線光標(biāo)的方法
這篇文章主要介紹了Python實現(xiàn)在matplotlib中兩個坐標(biāo)軸之間畫一條直線光標(biāo)的方法,涉及Python操作matplotlib模塊繪圖的相關(guān)技巧,需要的朋友可以參考下2015-05-05

