如何解釋“我篡改了區(qū)塊鏈”這個(gè)問題
區(qū)塊鏈數(shù)據(jù)“全局一致”、“難以篡改”這兩個(gè)特性已經(jīng)廣為人知,是區(qū)塊鏈營(yíng)造“信任”的基石。為了達(dá)到這兩個(gè)效果,區(qū)塊鏈的共識(shí)、同步、校驗(yàn)等技術(shù)細(xì)節(jié)足可大書特書,而本文要從“我篡改了區(qū)塊鏈數(shù)據(jù)”講起。
“我篡改了區(qū)塊鏈數(shù)據(jù)”
FISCO BCOS 開源聯(lián)盟鏈社區(qū)現(xiàn)在相當(dāng)活躍,每天都會(huì)產(chǎn)生大量的討論,大家也會(huì)饒有興趣地研究和挑戰(zhàn)區(qū)塊鏈如何做到“難以篡改”。我們注意到,尤其在 FISCO BCOS 支持 MySQL 數(shù)據(jù)庫作為數(shù)據(jù)存儲(chǔ)引擎后,隔一陣子就有同學(xué)在群里問:“我手動(dòng)修改了我節(jié)點(diǎn)連接的數(shù)據(jù)庫里某個(gè)狀態(tài)數(shù)據(jù),這是不是就是篡改了區(qū)塊鏈數(shù)據(jù)呢?”
直觀地舉個(gè)例,如鏈上有個(gè)智能合約,管理特定資產(chǎn)余額,在數(shù)據(jù)庫合約表里,經(jīng)過共識(shí)的 Alice 的余額本來是 100,這時(shí)有人打開 MySQL 客戶端,找到那個(gè)合約對(duì)應(yīng)的表,把 Alice 的余額更新成 10000。
這時(shí)他表示:“你看,我調(diào)用合約的查詢接口,查出來 Alice 的余額確實(shí)是 10000,這就不對(duì)了嘛,而且,鏈還在出塊,根本不防篡改嘛!”。
初步分析和解答
為何這類問題最近多起來了?我們分析了下,猜想主要是由于 MySQL 數(shù)據(jù)庫用戶基礎(chǔ)良好,體系比較成熟,給用戶提供友好的命令行或圖形化交互工具,F(xiàn)ISCO BCOS 提供了一種表風(fēng)格的合約開發(fā)模式,表結(jié)構(gòu)設(shè)計(jì)得清晰直觀,對(duì)于用戶來說,一方面理解和管理起來更容易了,另一方面順手更新甚至刪除一下都是小意思。
下圖僅為示例數(shù)據(jù),采用 KVTable 合約方式,創(chuàng)建了名為 t_kv_node
的合約表,系統(tǒng)自動(dòng)加了 u_
前綴,可見,這個(gè)表結(jié)構(gòu)和數(shù)據(jù)一目了然。
而之前只采用 LevelDB 或 RocksDB 作為存儲(chǔ)引擎,這兩個(gè)文件型數(shù)據(jù)庫交互工具比較少,在用戶面前的存在感不強(qiáng),操作相對(duì)晦澀,主要通過 API 編程訪問,數(shù)據(jù)用肉眼難以辨別的 Hash 鍵尋址,動(dòng)手修改數(shù)據(jù)庫的情形就少了一些(但也不是不可能)。
所以,熱點(diǎn)問題浮出水面,前提是用戶可以更方便地修改底層數(shù)據(jù)了,而不是這個(gè)問題之前不存在。
這時(shí)我們會(huì)建議用戶試一下,針對(duì) Alice 的余額,發(fā)起一個(gè)交易,比如給 Alice 充值,或者讓 Alice 轉(zhuǎn)賬操作,這時(shí),修改過數(shù)據(jù)的節(jié)點(diǎn)將無法參與共識(shí)。因?yàn)樵摴?jié)點(diǎn)上算出來的 Alice 的余額和其他節(jié)點(diǎn)結(jié)果不同,其他節(jié)點(diǎn)依舊按 100 的余額進(jìn)行計(jì)算,而不是 10000,顯然結(jié)果是對(duì)不齊的。
復(fù)習(xí)下 PBFT 的容錯(cuò)模型:定義 f
為可容錯(cuò)節(jié)點(diǎn)數(shù),網(wǎng)絡(luò)中共識(shí)節(jié)點(diǎn)總數(shù)應(yīng)等于或多于 3f+1
。即鏈上有 4 個(gè)共識(shí)節(jié)點(diǎn)時(shí),可容錯(cuò)的 f=1
;共識(shí)節(jié)點(diǎn)總數(shù)為 7 個(gè)時(shí),f=2
,以此類推。
如果未修改過數(shù)據(jù)的節(jié)點(diǎn)數(shù)滿足 PBFT 要求的 2f+1
的數(shù)量,鏈依舊可以出塊。但被修改過的節(jié)點(diǎn),一旦有交易涉及臟數(shù)據(jù),就像踩到了雷一樣,從此再無法與鏈共識(shí)、同步,相當(dāng)于被拋棄了。這種節(jié)點(diǎn)可以稱為“拜占庭節(jié)點(diǎn)”,即作惡或出錯(cuò)的節(jié)點(diǎn),具備節(jié)點(diǎn)準(zhǔn)入控制能力的聯(lián)盟鏈甚至?xí)菡纪ス?jié)點(diǎn)隔離出去。
還有一種可能性是,手動(dòng)修改了數(shù)據(jù)庫里的數(shù)據(jù),但節(jié)點(diǎn)內(nèi)存里還剛好緩存了一份副本,并沒有被修改,所以通過節(jié)點(diǎn)對(duì)這個(gè)數(shù)據(jù)的查詢、交易還是正常的,甚至?xí)谜_的結(jié)果把數(shù)據(jù)庫里被篡改過的數(shù)據(jù)覆蓋掉,不過這是概率性事件,取決于緩存的大小和當(dāng)時(shí)包含的數(shù)據(jù)項(xiàng)。
*注:對(duì)于采用PoW或其他共識(shí)機(jī)制的鏈來說,容錯(cuò)模型有所不同,但在容錯(cuò)范圍內(nèi)的少數(shù)節(jié)點(diǎn)被篡改,也不會(huì)影響鏈的共識(shí)。
“能否篡改整個(gè)聯(lián)盟鏈”
有的同學(xué)可能會(huì)繼續(xù)刨根問底:“那我多修改幾個(gè)節(jié)點(diǎn)的數(shù)據(jù)是不是就篡改了?”,一般提出這個(gè)問題的同學(xué)是面向他自己部署的開發(fā)測(cè)試環(huán)境,所有節(jié)點(diǎn)都在他手上,所以可以隨便改。
在真實(shí)的聯(lián)盟鏈環(huán)境上,節(jié)點(diǎn)分別掌握在不同機(jī)構(gòu)手里,要修改,首先得侵入他人的網(wǎng)絡(luò)、獲得服務(wù)器和數(shù)據(jù)庫權(quán)限、發(fā)起修改再全身而退。事實(shí)上,在注重安全防護(hù)的商業(yè)化環(huán)境里,這是非常艱難、幾乎不可能做到的事情。
從機(jī)構(gòu)粒度來看,單個(gè)機(jī)構(gòu)掌握的節(jié)點(diǎn)數(shù),應(yīng)該低于共識(shí)算法可容錯(cuò)的數(shù)量。比如,鏈上總共有 7 個(gè)共識(shí)節(jié)點(diǎn),那么單個(gè)機(jī)構(gòu)掌握的共識(shí)節(jié)點(diǎn)不應(yīng)多于 2 個(gè),這樣可以避免機(jī)構(gòu)內(nèi)部強(qiáng)行修改自己掌握的節(jié)點(diǎn)數(shù)據(jù),或一個(gè)機(jī)構(gòu)的所有節(jié)點(diǎn)都意外出錯(cuò)、掉線(比如機(jī)房光纖都被挖斷了),導(dǎo)致鏈無法出塊。
真的沒有辦法防“本地篡改”嗎?
考慮區(qū)塊鏈數(shù)據(jù)本地驗(yàn)證的機(jī)制,比如區(qū)塊之間的 Hash 關(guān)系、狀態(tài)的 Merkle 樹結(jié)構(gòu)、共識(shí)節(jié)點(diǎn)的簽名等,按數(shù)據(jù)的互驗(yàn)關(guān)系順藤摸瓜進(jìn)行檢測(cè),似乎有一定概率可以本地檢測(cè)出數(shù)據(jù)異常。
但進(jìn)一步想,對(duì)某個(gè)數(shù)據(jù)的查詢,區(qū)塊鏈的本地校驗(yàn)范圍是有限的,一般不會(huì)超出單個(gè)區(qū)塊或者一棵 Merkle 樹,所以如果篡改者比較熟悉區(qū)塊鏈數(shù)據(jù)的結(jié)構(gòu)和本地校驗(yàn)邏輯,也可以順著數(shù)據(jù)校驗(yàn)關(guān)系,從狀態(tài)值開始,把 Merkle 樹、區(qū)塊 Hash 等關(guān)鍵數(shù)據(jù)全部改掉。
甚至更徹底地,從創(chuàng)世塊開始,把所有的區(qū)塊、系統(tǒng)配置(對(duì)于 PoW,可以修改挖礦難度以加速出塊)、PBFT 的共識(shí)者列表等等,都按他的邏輯改一遍,這樣這條本地?cái)?shù)據(jù)鏈依舊是校驗(yàn)自洽的,只是無法和其他節(jié)點(diǎn)共識(shí)了。
這種改法,聽起來需要不少力氣活,但對(duì)于一個(gè)有決心、有能力的篡改者來說,改改本地?cái)?shù)據(jù)這個(gè)事情其實(shí)并不難,難的只是去改別的機(jī)構(gòu)數(shù)據(jù)而已。
到了這個(gè)份上,就相當(dāng)于一個(gè)人鐵了心要“騙自己”,那神仙都沒有辦法了。一旦把本地?cái)?shù)據(jù)修改的權(quán)限交給了不適當(dāng)?shù)娜?,最壞情況下,整條鏈沒有一個(gè)字節(jié)是對(duì)的。
但是,本地?cái)?shù)據(jù)再錯(cuò),也只會(huì)影響自己,影響不了別人,一旦和其他節(jié)點(diǎn)進(jìn)行共識(shí),就會(huì)被發(fā)現(xiàn),甚至被懲戒,整個(gè)效果會(huì)有一點(diǎn)掩耳盜鈴的意思。
“為什么區(qū)塊鏈不攔住我篡改數(shù)據(jù)?”
再進(jìn)一步,那位同學(xué)又會(huì)問:“為什么區(qū)塊鏈不能立刻發(fā)現(xiàn)、并且阻止我篡改數(shù)據(jù)?也許我只是無意手誤呢”。坦率說,這有點(diǎn)對(duì)區(qū)塊鏈期望過高了。
區(qū)塊鏈系統(tǒng)并非無所不能,也不會(huì)包辦一切,區(qū)塊鏈并不會(huì)阻止用戶對(duì)自己的服務(wù)器、軟件、數(shù)據(jù)庫等施加操作,就像法律不能也不應(yīng)去阻止你打碎家里的杯子一樣。
本質(zhì)上,區(qū)塊鏈的一致性、難以篡改性是面向“全局”的,是由多方博弈和協(xié)作達(dá)成的,當(dāng)鏈上交易牽涉錯(cuò)誤數(shù)據(jù)時(shí),共識(shí)機(jī)制可以檢測(cè)并拒絕已被篡改的數(shù)據(jù),保證鏈上剩余的大多數(shù)健康節(jié)點(diǎn)繼續(xù)共識(shí)出塊。而節(jié)點(diǎn)本地不參與共識(shí)的數(shù)據(jù),共識(shí)機(jī)制鞭長(zhǎng)莫及。
那么,區(qū)塊鏈為什么不能主動(dòng)檢測(cè)和糾正錯(cuò)誤,保證每個(gè)節(jié)點(diǎn)上的數(shù)據(jù)一致性?首先,鏈上的數(shù)據(jù)非常龐雜,用戶直接登入數(shù)據(jù)庫手動(dòng)修改少量數(shù)據(jù),區(qū)塊鏈節(jié)點(diǎn)并不知道哪一條數(shù)據(jù)被修改了,無法觸發(fā)檢查。
如果區(qū)塊鏈系統(tǒng)定期巡檢所有數(shù)據(jù),并將所有數(shù)據(jù)和其他節(jié)點(diǎn)進(jìn)行比對(duì),可想而知,這樣做的話,網(wǎng)絡(luò)、磁盤和計(jì)算開銷會(huì)非常大。
關(guān)鍵是,這并不解決問題,因?yàn)閺臄?shù)據(jù)被篡改后到檢測(cè)出來的時(shí)間窗里,哪怕臟數(shù)據(jù)只存在了幾十毫秒,但這時(shí)如果不幸有應(yīng)用來查詢數(shù)據(jù),依舊會(huì)得到篡改后的結(jié)果。對(duì)要求苛刻的業(yè)務(wù)來說,事后檢測(cè)未必是最佳選項(xiàng),因?yàn)橛锌赡芤呀?jīng)造成了業(yè)務(wù)損失,屆時(shí)能做的最多就是告警和沖正了。
當(dāng)然,也可以結(jié)合數(shù)據(jù)庫的操作監(jiān)控、binlog 等輔助機(jī)制,加速響應(yīng)速度和檢測(cè)效率。方法還是有的,如上所述,只是性價(jià)比較低,也不徹底解決問題,只有對(duì)數(shù)據(jù)修改極其敏感,且業(yè)務(wù)上接受延時(shí)發(fā)現(xiàn)和修訂的特定場(chǎng)景,才會(huì)考慮將其作為補(bǔ)救措施。我們把這部分歸類到運(yùn)營(yíng)管理工具里,根據(jù)場(chǎng)景需求來實(shí)現(xiàn)。
還有一種方法,可以部分解決查詢問題:f+1 查詢。即查詢數(shù)據(jù)時(shí),無論是查區(qū)塊數(shù)據(jù),還是合約的狀態(tài)數(shù)據(jù),不妨多查幾個(gè)節(jié)點(diǎn),查詢節(jié)點(diǎn)數(shù)多于 f 即可。
如鏈上有個(gè) 7 個(gè)節(jié)點(diǎn)時(shí),f=2,用戶查詢自己節(jié)點(diǎn)之外,繼續(xù)發(fā)出網(wǎng)絡(luò)請(qǐng)求去查詢其他機(jī)構(gòu)的 2 個(gè)節(jié)點(diǎn),共查詢 3 個(gè)節(jié)點(diǎn),如果得到的數(shù)據(jù)都是一致的,則表示數(shù)據(jù)一定是正確的,反之,一定是這 3 個(gè)節(jié)點(diǎn)里出了問題。
但是,要執(zhí)行 f+1 查詢,前提是其他機(jī)構(gòu)開了查詢接口權(quán)限,讓你連接上去查詢。在很多安全防護(hù)嚴(yán)密的聯(lián)盟鏈上,一般只打開節(jié)點(diǎn)之間 P2P 互聯(lián)的網(wǎng)絡(luò)端口,不會(huì)輕易給其他機(jī)構(gòu)提供數(shù)據(jù)查詢權(quán)限。再則,在網(wǎng)絡(luò)上發(fā)起多次查詢,其異步性、時(shí)效性、成功率和性能表現(xiàn)都會(huì)帶來更多變數(shù)。
綜上所述,對(duì)節(jié)點(diǎn)本地的數(shù)據(jù),就像打地鼠,冒頭的(發(fā)出交易參與共識(shí),或進(jìn)行 f+1 查詢),區(qū)塊鏈全局共識(shí)和容錯(cuò)機(jī)制能發(fā)現(xiàn),沒有冒頭僅蹲在用戶硬盤里的,只能用戶自己負(fù)責(zé)了。
結(jié)語
區(qū)塊鏈通過網(wǎng)絡(luò)博弈、多方校驗(yàn)實(shí)現(xiàn)了全網(wǎng)的容錯(cuò)防作惡,而區(qū)塊鏈同步給到各節(jié)點(diǎn)的本地?cái)?shù)據(jù),需要用戶自行妥善管理保存。
從信任傳遞來看,首先用戶得“信自己”,如果連自己都無法相信,說明系統(tǒng)和數(shù)據(jù)管理有漏洞,莫說是修改數(shù)據(jù)了,在本地系統(tǒng)的整個(gè)鏈路上,包括區(qū)塊鏈軟件、SDK、業(yè)務(wù)服務(wù)都有可能出錯(cuò)和篡改作假,這樣的環(huán)境有何信任可言?
節(jié)點(diǎn)持有者必須非常審慎,首先不要手癢或手誤去改數(shù)據(jù),然后關(guān)鍵是要建立周全的制度,包括管理策略和技術(shù)防護(hù),比如,主機(jī)訪問控制、數(shù)據(jù)庫登錄和操作權(quán)限控制、操作審計(jì)、日志審計(jì)等,以避免本機(jī)構(gòu)內(nèi)有人越權(quán)訪問監(jiān)守自盜,或者被外部滲透。
萬一數(shù)據(jù)出錯(cuò),區(qū)塊鏈比中心化系統(tǒng)好一點(diǎn)的就是,還有可能通過與其他節(jié)點(diǎn)互相校驗(yàn)檢測(cè)出來,這時(shí)則應(yīng)該進(jìn)行告警、查證、補(bǔ)正和追責(zé),以及在有條件和有必要的前提下,善用 f+1 查詢方法,給查詢操作加一點(diǎn)點(diǎn)保險(xiǎn)。
另外,建議定期備份節(jié)點(diǎn)數(shù)據(jù)到安全的離線設(shè)備上,這樣無論是出現(xiàn)意外還是人為的數(shù)據(jù)問題,依舊可以從冷數(shù)據(jù)里快速恢復(fù),保證一定的 RTO(復(fù)原時(shí)間目標(biāo))和 RPO(恢復(fù)點(diǎn)目標(biāo))。
而區(qū)塊鏈的健壯性在于,無論單個(gè)角色怎么折騰自己的節(jié)點(diǎn)和數(shù)據(jù),對(duì)全局是沒有影響的,只有修改者自己受損。任憑窩里翻天覆地,鏈上依舊云淡風(fēng)輕,其“全局一致”、“難以篡改”的定律依舊成立,鏈仍然是信任的錨點(diǎn),這就是區(qū)塊鏈的魅力。