這篇文章是基于Certificate Transparency 來寫的砌烁。
之前我們介紹了一些內(nèi)部系統(tǒng)的分布式設(shè)計(jì),比如RAFT,比如SPANNER 或者DYNAMO哺哼。里面有一個(gè)假設(shè)所有節(jié)點(diǎn)都是值得信任的。
當(dāng)一個(gè)系統(tǒng)是開放的時(shí)候叼风,這個(gè)假設(shè)可能就不對(duì)了取董。當(dāng)有節(jié)點(diǎn)存在欺騙的時(shí)候,我們要引入拜占庭協(xié)議无宿。這是個(gè)復(fù)雜的協(xié)議茵汰,代價(jià)很高。據(jù)我所知孽鸡,現(xiàn)在只有在航空航天領(lǐng)域被運(yùn)用蹂午。
同時(shí)我們知道現(xiàn)在我們?cè)诨ヂ?lián)網(wǎng)上沖浪時(shí),大多數(shù)網(wǎng)站都會(huì)采用HTTPS彬碱。
這個(gè)協(xié)議通過證書隨后走非對(duì)稱加密隨后交換密鑰豆胸,所有通信都可以加密傳輸。使得中間人攻擊可以很大程度的預(yù)防巷疼。這里我們要介紹的這個(gè)系統(tǒng)就是要解決證書不可信的問題的晚胡。在此之前,讓我們先回到1995年,那時(shí)還沒有證書和HTTPS估盘,那時(shí)會(huì)發(fā)生什么瓷患?
1.A發(fā)送給B一條消息,卻被H(壞人)截獲:
A“嗨遣妥,B擅编,我是A。給我你的公鑰” --> H --x--> B
2.H將這條截獲的消息轉(zhuǎn)送給B燥透;此時(shí)鮑伯并無法分辨這條消息是否從真的A那里發(fā)來的:
A --x--> H “嗨沙咏,B,我是A班套。給我你的公鑰” --> B
3.B回應(yīng)A的消息肢藐,并附上了他的公鑰:
A<--x--- H<-- [B的公鑰]-- B
4.H用自己的密鑰替換了消息中B的密鑰,并將消息轉(zhuǎn)發(fā)給A吱韭,聲稱這是B的公鑰:
A<-- [H的公鑰]-- H <--x--B
5.A用她以為是B的公鑰加密了她的消息吆豹,以為只有B才能讀到它:
A“我們?cè)诠财囌疽娒妫 ?-[使用H的公鑰加密] --> H --x-->B
6.然而理盆,由于這個(gè)消息實(shí)際上是用H(壞人)的密鑰加密的痘煤,所以H可以解密它,閱讀它猿规,并在愿意的時(shí)候修改它衷快。他使用B的密鑰重新加密,并將重新加密后的消息轉(zhuǎn)發(fā)給B:
A--x--> H“在家等我姨俩!”--[使用B的公鑰加密] --> B
7.B認(rèn)為蘸拔,這條消息是經(jīng)由安全的傳輸通道從A那里傳來的。
這個(gè)例子顯示了A和B需要某種方法來確定他們是真正拿到了屬于對(duì)方的公鑰环葵,而不是拿到來自攻擊者的公鑰调窍。否則,這類攻擊一般都是可行的张遭,在原理上邓萨,可以針對(duì)任何使用公鑰——密鑰技術(shù)的通訊消息發(fā)起攻擊。
那么如何防護(hù)這種攻擊呢菊卷?
公鑰可以由"數(shù)字證書認(rèn)證機(jī)構(gòu)"驗(yàn)證缔恳,這些公鑰通過安全的渠道(例如,隨Web瀏覽器或操作系統(tǒng)安裝)分發(fā)洁闰。公共密鑰也可以經(jīng)由Web在線信任進(jìn)行在線驗(yàn)證褐耳,可以通過安全的途徑分發(fā)公鑰(例如,通過面對(duì)面的途徑分發(fā)公鑰)
如何做呢渴庆?
當(dāng)客戶端連接到支持TLS協(xié)議的服務(wù)器要求創(chuàng)建安全連接并列出了受支持的密碼包(包括加密算法铃芦、散列算法等)雅镊,握手開始。
服務(wù)器從該列表中決定密碼包刃滓,并通知客戶端仁烹。
服務(wù)器發(fā)回其數(shù)字證書,此證書通常包含服務(wù)器的名稱咧虎、受信任的證書頒發(fā)機(jī)構(gòu)(CA)和服務(wù)器的公鑰卓缰。
客戶端確認(rèn)其頒發(fā)的證書的有效性。
為了生成會(huì)話密鑰用于安全連接砰诵,客戶端使用服務(wù)器的公鑰加密隨機(jī)生成的密鑰征唬,并將其發(fā)送到服務(wù)器,只有服務(wù)器才能使用自己的私鑰解密茁彭。
利用隨機(jī)數(shù)总寒,雙方生成用于加密和解密的對(duì)稱密鑰。這就是TLS協(xié)議的握手理肺,握手完畢后的連接是安全的摄闸,直到連接(被)關(guān)閉。如果上述任何一個(gè)步驟失敗妹萨,TLS握手過程就會(huì)失敗年枕,并且斷開所有的連接。
證書包含什么信息呢乎完?
一個(gè)證書里通常會(huì)包含DNS名熏兄,比如(gmali.com),這個(gè)服務(wù)器的公鑰树姨,CA的identity, 和一個(gè)用這個(gè)CA私鑰簽的名霍弹。
瀏覽器會(huì)包含一組授信的CA的公鑰當(dāng)瀏覽器使用HTTPS發(fā)起連接。
服務(wù)器會(huì)發(fā)來證書娃弓,瀏覽器會(huì)檢查這個(gè)證書,隨后會(huì)用證書里的公鑰去加密來確保那個(gè)服務(wù)器具備私鑰岛宦。這樣引入了CA的概念就可以防止中間人攻擊了台丛。因?yàn)橹虚g人要扮演B,就必須要知道B的私鑰砾肺。因?yàn)锽的私鑰是注冊(cè)在CA里的挽霉。
證書為什么還不夠?
因?yàn)槿绾螞Q定誰持有一個(gè)DNS NAME变汪,不是那么直接的事情侠坎。比如我告訴CA,我是‘xy.com’的持有人裙盾,我需要申請(qǐng)一個(gè)證書实胸。CA怎么認(rèn)證我是真的持有人呢
更加嚴(yán)重的情況是他嫡,一般瀏覽器里會(huì)有100多個(gè)授信CA,不是所有的CA都能規(guī)范的運(yùn)作(比如有一個(gè)不值得信任的員工進(jìn)入某個(gè)CA庐完,就可以上傳壞人的證書進(jìn)去)
所以整個(gè)CA的體系的健壯程度就取決于它最薄弱的一環(huán)钢属。
Certificate Transparency 如何解決問題
首先要介紹2個(gè)組件。一個(gè)是CT LOG SERVER门躯。這個(gè)組件當(dāng)一個(gè)用戶向CA申請(qǐng)證書之后淆党,都會(huì)往CT LOG里寫一條記錄。
同時(shí)又另外一個(gè)監(jiān)控組件讶凉,專門用來發(fā)現(xiàn)一個(gè)DNS NAME 是否被不合法的人注冊(cè)了染乌,如果發(fā)現(xiàn)就立刻報(bào)警。
所以瀏覽器去連接一個(gè)服務(wù)器的時(shí)候懂讯,拿到了那個(gè)服務(wù)器的證書荷憋,他還回去LOG SERVER 去求證一下,是否這個(gè)證書在LOG SERVER里存在域醇。 存在才繼續(xù)連接台谊。
而MONITOR會(huì)時(shí)刻監(jiān)控這個(gè)LOG SERVER的LOG,來發(fā)現(xiàn)有可疑的證書注冊(cè)進(jìn)去譬挚。
因?yàn)橛斜O(jiān)控保護(hù)锅铅,所以瀏覽器可以認(rèn)為被LOG SERVER認(rèn)證了的證書可以安全使用,如果不安全我會(huì)收到報(bào)警减宣。
下面就是一個(gè)新的問題盐须,如何確保所有人看到同樣的LOG。
因?yàn)橐恍阂獾腃A漆腌,可以搭配惡意的日志操作者來繼續(xù)做壞事贼邓。
比如惡意的CA委托日志操作者幫他寫假CA進(jìn)LOG SERVER,當(dāng)瀏覽器驗(yàn)證完闷尿,再立刻把他刪除塑径。這樣有個(gè)窗口可能監(jiān)控還沒來得及發(fā)現(xiàn)那條記錄就被刪了。那么瀏覽器就拿著假CA在通訊填具,就會(huì)被中間人攻擊统舀。
還有一種攻擊手段就是日志操作者使得日志服務(wù)器讓瀏覽器看到的是帶問題的日志的LOG。讓監(jiān)控者看到的是不帶問題的LOG劳景。這樣做也可以達(dá)到之前說的效果誉简。
要防止第一點(diǎn),我們必須構(gòu)建一個(gè)不可修改(刪除)的日志盟广,這個(gè)日志要求只可以APPEND闷串。
如何來做到這一點(diǎn)呢,萬一就是有個(gè)人去修改了日志的中間部分呢筋量?
merkle tree
可見其子節(jié)點(diǎn)是每個(gè)數(shù)據(jù)項(xiàng)或者一批數(shù)據(jù)項(xiàng)(數(shù)據(jù)塊)對(duì)應(yīng)的哈希值烹吵,中間節(jié)點(diǎn)則保存對(duì)其所有子節(jié)點(diǎn)哈希值再次進(jìn)行哈希運(yùn)算后的值碉熄,依次由下往上類推,直到根節(jié)點(diǎn)年叮,其保存的Top Hash代表整棵樹的哈希值具被,也就是所有數(shù)據(jù)的整體哈希值。具體使用的時(shí)候只损,既可以像例子中一樣是一個(gè)二叉樹一姿,也可以是多叉樹。
Merkle樹常用于快速偵測(cè)部分?jǐn)?shù)據(jù)正吃颈梗或者異常的變動(dòng)叮叹。當(dāng)某個(gè)底層數(shù)據(jù)發(fā)生變化時(shí),其對(duì)應(yīng)Merkle樹的子節(jié)點(diǎn)哈希值會(huì)跟著變化爆存,子節(jié)點(diǎn)的父節(jié)點(diǎn)哈希值也隨之變化蛉顽,依此類推,直到根節(jié)點(diǎn)先较,其間經(jīng)過的節(jié)點(diǎn)哈希值都發(fā)生變化携冤,但是其他無關(guān)樹節(jié)點(diǎn)哈希值并不發(fā)生改變。通過Merkle樹闲勺,可以O(shè)(log(n))時(shí)間內(nèi)快速定位變化的數(shù)據(jù)內(nèi)容曾棕。
有這樣一棵樹,只要你修改了之前的某個(gè)LOG菜循,那么樹的根節(jié)點(diǎn)的HASH必然會(huì)改變翘地。
LOG SERVER 會(huì)對(duì)樹的根做簽名,一旦LOG SERVER 把這個(gè)簽名的樹根HASH給到我癌幕,之后他就無法抵賴了衙耕。
隨著新的LOG不斷進(jìn)來,可能會(huì)產(chǎn)生新的根勺远。比如有N個(gè)條目在LOG里組成了一個(gè)樹橙喘。后面的N個(gè)LOG又會(huì)組成另一個(gè)樹。這時(shí)需要把這2個(gè)樹的根合并起來胶逢,那么就會(huì)產(chǎn)生一個(gè)新的根厅瞎。
瀏覽器會(huì)問LOG SERVER,證書A 是否在LOG 中宪塔。 如果LOG SERVER承認(rèn)的話,他必須要給出這個(gè)A的位置囊拜,以及對(duì)應(yīng)需求用到的驗(yàn)證HASH值的一組HASH
比如證書的位置在下圖紅圈中某筐,那么LOG SERVER 要恢復(fù)的位置是3,然后和綠色圈出來的這組HASH值給瀏覽器冠跷。
瀏覽器可以根據(jù)自己手里的證書自己算出一個(gè)HASH值南誊,然后一直往上求出根節(jié)點(diǎn)的HASH身诺,再去和服務(wù)器簽名的那個(gè)根節(jié)點(diǎn)的HASH做比較。如果一致抄囚,就代表數(shù)據(jù)沒有被修改過霉赡。
這個(gè)驗(yàn)證的過程還是比較快的,比如說有N個(gè)LOG幔托,只需要LOG(N) 次HASH就可以得到結(jié)果穴亏。這個(gè)非常重要,因?yàn)閷?duì)瀏覽器來說他也不想從LOG SERVER那邊去下載所有的LOG重挑,來判斷LOG 是不是被篡改嗓化。
服務(wù)器有沒有可能撒謊
比如他自己維護(hù)了一個(gè)假的STH,里面也包含了那個(gè)非法的證書谬哀,然后把假的STH告訴瀏覽器刺覆,之后驗(yàn)證就可以用改過的HASH了。同時(shí)還要不被監(jiān)控者發(fā)現(xiàn)史煎。
這里就類似于GIT的分支谦屑,出現(xiàn)了分叉。兩個(gè)人在不同的版本上篇梭。如何防止這種問題呢氢橙?
這里就要引入另一個(gè)技術(shù)叫GOSSIP
GOSSIP PROTOCOL
更新的信息經(jīng)過一定輪數(shù)(Round)的傳播后,集群內(nèi)所有節(jié)點(diǎn)都會(huì)獲得全局最新信息很洋。節(jié)點(diǎn)P隨機(jī)選擇集群中另外一個(gè)節(jié)點(diǎn)Q充蓝,然后與Q交換更新信息;Q如果信息有更新喉磁,則類似P一樣傳播給任意其他節(jié)點(diǎn)(此時(shí)P也可以再傳播給其他節(jié)點(diǎn))谓苟,這樣經(jīng)過一定輪數(shù)的信息交換辽旋,更新的信息就會(huì)快速傳播到整個(gè)網(wǎng)絡(luò)節(jié)點(diǎn)耿焊。其傳播過程就是我們常說的“一傳十,十傳百”的模式距潘。
有了這個(gè)協(xié)議后孕暇,所有節(jié)點(diǎn)都可以交換服務(wù)器告訴他們的ROOT HASH值仑撞,這個(gè)值可能會(huì)合法的不一樣,也就是說有一個(gè)節(jié)點(diǎn)的HASH值是另一個(gè)的前綴(也就是我們上文提到的妖滔,ROOT HASH隨著LOG增多可能會(huì)擴(kuò)展一層上去)
如果不是前綴隧哮,還不一樣,那么一定代表有FORK問題發(fā)生了(出現(xiàn)了2個(gè)分支)
這的正確性是基于加密型HASH座舍,LOG SERVER找不到另一個(gè)錯(cuò)誤的值使得和正確的值都能夠HASH出同一個(gè)ROOT HASH沮翔。
同時(shí)前綴問題是這樣,如果CLIENT發(fā)現(xiàn)2個(gè)ROOT HASH值不一樣曲秉,那么就需要LOG SERVER 提供采蚀,一組右側(cè)根的HASH疲牵,使得一個(gè)ROOT HASH可以被算出來。
如下圖榆鼠。
前綴的證明方法就是要求LOG SERVER 提供2個(gè)綠色的HASH值纲爸,使得CLIENT可以驗(yàn)證。
此外如果LOG SERVER 對(duì)LOG 做了手腳妆够。
比如正確的右側(cè)綠色的HASH值為X识啦。做手腳那塊區(qū)域的的正確的HASH 為H1。
那么另一個(gè)不一樣的正確的ROOT HASH為H2
有H2 = H(H1, X)
如果要對(duì)H1做手腳责静,H1會(huì)變成H11
那么就要求LOG SERVER 能找到一個(gè)Y使得 H2 = H(H11, Y)
不然沒法向CLIENT交代這個(gè)前綴正確性袁滥。
根據(jù)加密型HASH,這給反向計(jì)算也是做不到的灾螃。
所以只要基于這個(gè)算法题翻,只要瀏覽器和監(jiān)控器做了足夠多GOSSIP。最終他們能確保他們看到相同的LOG腰鬼。
LOG SERVER 最后能使的壞就是嵌赠,他在一開始給了CLIENT一個(gè)錯(cuò)的ROOT HASH,和一個(gè)錯(cuò)的證書熄赡,及一組錯(cuò)的認(rèn)證姜挺。然后他用了之后,LOG SERVER 要趕在他能和MONITOR通信GOSSIP之前彼硫,立刻去更新這個(gè)ROOT HASH 和正確的證書炊豪。這樣他之后在和MONITOR 同步的時(shí)候,就不會(huì)發(fā)現(xiàn)問題拧篮。
為了防止這種情況词渤,所以CLIENT必須要記錄下之前LOG SERVER給的每一個(gè)簽名的ROOT HASH。然后更新的時(shí)候必須要求使用前綴認(rèn)證算法串绩。就是你要更新的ROOT HASH缺虐,得證明是我當(dāng)前這個(gè)ROOT HASH基礎(chǔ)上生成出來的。
這樣LOG SERVER 就沒法FORK一個(gè)分支出來了礁凡。這個(gè)又被成為FORK CONSISTENCY高氮。
當(dāng)這個(gè)證明被違反時(shí),就代表這個(gè)LOG SERVER可能是不懷好意的顷牌,或者被污染的剪芍。這時(shí)候就需要下線這個(gè)LOG SERVER。
總結(jié)
- 這套系統(tǒng)的核心就是使得所有人看到同樣的日志窟蓝,可以防止壞人篡改罪裹。
- 如果瀏覽器能看到假證書,那么DNS NAME OWNER也可以發(fā)現(xiàn),并報(bào)警坊谁。
- 如果預(yù)防很困難,事后監(jiān)控就值得被考慮滑臊。
- 哈希樹+gossip是經(jīng)常組合使用的分布式技巧口芍。