什么是反序列化?
有些時(shí)候我們需要把應(yīng)用程序中的數(shù)據(jù)以另一種形式進(jìn)行表達(dá)搂捧,以便于將數(shù)據(jù)存儲(chǔ)起來(lái)扮宠,并在未來(lái)某個(gè)時(shí)間點(diǎn)再次使用,或者便于通過(guò)網(wǎng)絡(luò)傳輸給接收方松靡。這一過(guò)程我們把它叫做序列化简僧。典型的例子是,用戶數(shù)據(jù)被序列化后存儲(chǔ)到數(shù)據(jù)庫(kù)中雕欺,另一個(gè)例子是在Stateless架構(gòu)下岛马,用戶登陸后的身份數(shù)據(jù)被序列化存儲(chǔ)到了瀏覽器中。
反序列化和序列化是兩個(gè)正好相反的過(guò)程屠列。當(dāng)我們要再次使用這些數(shù)據(jù)的時(shí)候啦逆,應(yīng)用程序讀取序列化之后的數(shù)據(jù),并將其恢復(fù)成應(yīng)用程序中的數(shù)據(jù)笛洛。例如服務(wù)器端從Redis中讀出一個(gè)鍵值對(duì)夏志,其內(nèi)容是JSON格式的字符串,代表了某個(gè)用戶的個(gè)人資料苛让,并將其恢復(fù)成應(yīng)用程序可使用的數(shù)據(jù)沟蔑。
反序列化有什么安全問(wèn)題?
盡管反序列化最嚴(yán)重可導(dǎo)致遠(yuǎn)程代碼執(zhí)行(RCE蝌诡,Remote Code Execution)溉贿,但最常見(jiàn)的反序列化安全問(wèn)題卻是通過(guò)修改序列化之后的數(shù)據(jù)字段,從而進(jìn)行提權(quán)或越權(quán)操作浦旱。
舉例來(lái)說(shuō)宇色,服務(wù)器端為了能快速橫向擴(kuò)展而被設(shè)計(jì)成了后端無(wú)服務(wù)狀態(tài)架構(gòu),這也就意味著用戶登陸后,其身份信息(例如用戶ID宣蠕,姓名例隆,角色,登陸時(shí)間戳等)被保存到了瀏覽器cookie當(dāng)中抢蚀,在后續(xù)的請(qǐng)求里將會(huì)被自動(dòng)發(fā)往服務(wù)器镀层。
圖:用戶登陸后,服務(wù)器將用戶身份信息存儲(chǔ)在瀏覽器cookie中
存儲(chǔ)于cookie中的這份數(shù)據(jù)的格式是應(yīng)用程序自定義的皿曲,但攻擊者通過(guò)探索嘗試后發(fā)現(xiàn)唱逢,修改其中的某個(gè)字段就能將用戶從普通用戶修改為管理員。
存儲(chǔ)于cookie中的原始數(shù)據(jù):
Cookie: 3844998|AliceM|y|27|*NU*|active|null|201809
經(jīng)過(guò)修改后的數(shù)據(jù)
Cookie: 3844998|AliceM|y|27|*ADMIN*|active|null|201809
由于缺乏對(duì)數(shù)據(jù)完整性的校驗(yàn)屋休,服務(wù)器端在收到被修改過(guò)的這段數(shù)據(jù)后坞古,就把當(dāng)前用戶當(dāng)作ADMIN用戶來(lái)處理了。
這些地方有反序列化安全問(wèn)題
上面舉的例子是瀏覽器存儲(chǔ)cookie中的數(shù)據(jù)可能遭受反序列化的攻擊劫樟,但還有其他很多地方也可能發(fā)生這樣的攻擊痪枫。例如HTTP請(qǐng)求body中的表達(dá)了某個(gè)或某些數(shù)據(jù)的JSON字符串,甚至Form表單中的數(shù)據(jù)某種程度上也是數(shù)據(jù)經(jīng)過(guò)反序列化后的表達(dá)形式叠艳。
抽象來(lái)看奶陈,只要是從Application之外讀取或接收數(shù)據(jù),并將其反序列化成Application或API中的對(duì)象附较,都可能存在反序列化安全問(wèn)題吃粒。
因此,下面這些地方同樣可能存在反序列化安全隱患拒课,但很可能不常被關(guān)注到:
- 自定義的HTTP Header
- 存儲(chǔ)在Redis声搁、MongoDB、MySQL等數(shù)據(jù)庫(kù)里的數(shù)據(jù)捕发,可能被不懷好意的員工修改
- 存儲(chǔ)在服務(wù)器本地,或者某個(gè)遠(yuǎn)程文件服務(wù)器里的文件很魂,可能被攻擊者替換
- 緩存服務(wù)器中的數(shù)據(jù)可能被攻擊者污染
怎么防御反序列化攻擊扎酷?
治病需要除根,能從根本上阻止反序列化安全問(wèn)題的防御方案就是完整性校驗(yàn)遏匆,而最常見(jiàn)的例子之一就是JWT法挨。
我們知道JWT由3部分組成:Header,Payload幅聘,Verify Signature凡纳。最后的簽名部分其實(shí)就是對(duì)數(shù)據(jù)進(jìn)行完整性校驗(yàn)的關(guān)鍵部分。
圖:JWT基本結(jié)構(gòu)
服務(wù)器端在接受到JWT之后帝蒿,首先用secret對(duì)數(shù)據(jù)部分進(jìn)行哈希計(jì)算荐糜,隨后檢查計(jì)算出來(lái)的哈希值是否和請(qǐng)求中的JWT簽名部分的哈希值相同。若兩者一致則認(rèn)為數(shù)據(jù)完整性沒(méi)有被破壞,若兩者有差異則說(shuō)明數(shù)據(jù)被修改過(guò)暴氏。
如果攻擊者想要憑空偽造一個(gè)JWT延塑,或者想修改JWT中的數(shù)據(jù),但由于計(jì)算哈希值的secret只有服務(wù)器端才知道答渔,因此攻擊者無(wú)法偽造出合法的簽名字段关带,進(jìn)而這樣有問(wèn)題的JTW很容易就能被服務(wù)器端識(shí)別出來(lái)。
值得注意的是沼撕,完整性校驗(yàn)還需要把數(shù)據(jù)結(jié)構(gòu)也包含進(jìn)來(lái)宋雏,這是因?yàn)楣粽呖赡軙?huì)修改序列化后的數(shù)據(jù)的結(jié)構(gòu),而不僅僅只是數(shù)據(jù)务豺。
其他防御措施
除此之外其他有助于防御反序列化安全問(wèn)題的措施磨总,但并不能完美的做到事前預(yù)防,例如:
- 反序列化之前冲呢,先進(jìn)行嚴(yán)格的數(shù)據(jù)類型校驗(yàn)舍败。由于校驗(yàn)規(guī)則容易被攻擊者探索出來(lái),進(jìn)而容易被繞過(guò)敬拓,因此防御不能僅依賴這一個(gè)手段邻薯,但可以作為完整性校驗(yàn)防御方案的補(bǔ)充。
- 對(duì)反序列化過(guò)程進(jìn)行詳盡的日志記錄乘凸,用以安全審計(jì)或調(diào)查厕诡。
- 監(jiān)控反序列化過(guò)程,在發(fā)現(xiàn)疑似反序列化攻擊時(shí)進(jìn)行警報(bào)营勤。
誤區(qū)
HTTPS自帶了完整性校驗(yàn)灵嫌,一旦有人修改或替換了請(qǐng)求內(nèi)容,就會(huì)被識(shí)別出來(lái)葛作,所以作為開(kāi)發(fā)團(tuán)隊(duì)寿羞,是不是就可以不用關(guān)心反序列化防御了?
HTTPS確實(shí)為數(shù)據(jù)傳輸提供了強(qiáng)有力的安全保護(hù)赂蠢,但它只能對(duì)傳輸中的數(shù)據(jù)進(jìn)行保護(hù)绪穆,能避免出現(xiàn)中間人攻擊(Man-In-The-Middle),然而反序列化攻擊卻往往是在數(shù)據(jù)傳輸之前進(jìn)行的虱岂,因此HTTPS并不能提供足夠的防護(hù)玖院。
這就猶如HTTPS只是一個(gè)盡心盡力的快遞公司,它能安全的把貨物從甲地運(yùn)送到乙地第岖,它可以保證遞送過(guò)程中貨物不被人調(diào)包难菌,但如果攻擊者交給快遞公司的貨物就是有問(wèn)題的刽锤,那快遞公司對(duì)此也無(wú)能為力硫眨,只要不是違禁品,它只能老老實(shí)實(shí)的把有問(wèn)題的貨物遞交給收貨方嗓奢。