網際網路架構實踐:給飛機換引擎和安全意識十原則

技術瑣話發表於2018-12-20

網際網路架構實踐:給飛機換引擎和安全意識十原則

本文有兩個部分,先介紹一下給飛機換引擎這個事情我的一些經驗,因為篇幅較短,然後介紹一下安全意識方面的一些心得。

給飛行中的飛機換引擎

所謂給飛行中的飛機(或飛馳的汽車)換引擎說的是我們需要對一個正在飛速發展的系統進行大幅度的架構改造,比如把All-in-one的架構改造成微服務架構,儘可能減少或消除停服的時間。一般而言,我們可以這麼來考慮方案,從重構的徹底性來說,分為這麼幾種:

  • 徹底重新做,直接從前到後拋棄老系統

  • 大規模重構,保留對使用者的這層皮,後面從服務到資料全部替換

  • 小規模重構,保留對使用者的這層皮以及資料結構,逐一替換核心邏輯到微服務

在做換引擎方案選擇和設計的時候需要考慮到這麼幾個現實的情況:

  • 業務需要發展。意味著會不斷有新需求需要開發,如果重構的時間拖的很長的話,我們需要在這段很長的時間內為兩套系統同時做新需求,新的系統還要不斷開發新需求,會增加更多的時間,如果新系統的開發不夠快的話,甚至一直上不了線。我們最好在重構之前對新增業務有一條界限,新系統只是覆蓋到這個版本做需求封板,然後和產品商量出一個妥協是否對於之後我們留1周作為系統切換期,這段時間不上線新需求。這1周分成幾個環節,需要2天的時間來做系統切換,然後需要5天的時間來觀察新系統的穩定性,修復新系統的Bug,隨後我們才能認為系統正式切換成功,把時間精力放到新業務的開發上。

網際網路架構實踐:給飛機換引擎和安全意識十原則

  • 資料需要遷移。我們是為一箇舊飛機在換引擎,無法拋棄飛機上的乘客。如果我們更改資料結構的話,需要對資料進行遷移。我們需要做下面的工作:

    • 準備遷移指令碼

    • 準備快取預熱指令碼

    • 使用既有的資料來測試這2個指令碼,然後觀察新系統的執行情況

    • 做反向資料遷移的指令碼,我們要考慮到切換到新系統後執行不流暢需要整體回滾的情況,這個時候我們需要把資料從新的資料庫遷移回老的資料庫

這種方式的遷移是需要有短暫的停機的,兩個指令碼的執行和驗證需要一段時間(資料量大的話導資料費時)。如果希望儘可能減少停機時間的話可以採用兩段走的方式,先同步今天前的99.9%的資料到新資料庫,在停機後再同步今天發生的那0.1%的資料。極端點希望徹底不停機的話,需要讓業務同時走兩套系統進行雙寫,這種方案大大增加複雜度,但是可以換來幾乎0停機的時間,除非是需要24小時線上的金融系統,一般不會考慮新老系統雙活的方案。

網際網路架構實踐:給飛機換引擎和安全意識十原則

你可能會覺得對於重構,我們應該考慮不要改底層資料庫,這會大大增加複雜性。這還是取決於我們希望多徹底進行重構,很多時候底層的資料庫設計不夠靈活這是最致命的,如果不切換到新的資料庫,即使搞成了微服務架構資料還是揉在一起,只是形態上是微服務,底層還是亂七八糟,這堆業務程式碼最終還是要進行一次重構。

  • 最好沒有停機讓使用者沒有感知。沒有停機意味著老的系統一直線上上穩定執行,新的系統可以以獨立的形態重新部署一套,使用者對我們新系統的開發沒有感知。新老系統的面子可以長得一模一樣,但是底層的呼叫面目全非。由於兩套系統都線上上執行,我們可以在內部對新系統進行充分測試,還可以比較同樣的業務流程在資料呈現上是否有差異。可惜現實往往沒有想象的這麼簡單,很多時候我們會依賴各種三方資料,對於金融系統來說往往對接了銀行的系統,這不是依賴這麼簡單了,而是完完全全的依賴。如果我們依賴的第三方會受到我們重構的影響,或是我們就是在切換依賴方的話,是否停機就不是自己可以掌控的了,第三方需要有停機我們就只能停機。這個時候我們能做的是,讓網站進行部分功能的停機而不是整體停機,使用者可以以只讀形式使用網站的所有功能,但是不能做寫入操作,待三方系統切換完成後切換到新系統了才能使用所有功能,這樣使用者也會有安全感。

網際網路架構實踐:給飛機換引擎和安全意識十原則

  • 萬一遷移失敗怎麼辦。之前說了我們遷移資料需要考慮回滾方案,這裡還會有一些比較噁心的點是如果新老系統有一些內部外部依賴是公用的話,儘量要隔離清楚,讓新老系統徹底獨立,這樣才好回滾,如果遷移後新系統在跑了汙染了老系統的資料,這個時候再要回滾會出現回不去的情況,因為老系統無法適應新的資料。內部要進行徹底的隔離是比較簡單,對於三方的資料(比如CDN)我們也要考慮到資料在邊緣節點和使用者端快取的情況,在設計方案的時候就要考慮到切換後回退的可能性,儘可能讓兩套系統使用獨立的資料不要產生資料覆蓋的情況造成汙染。

還是要具體的事情具體來分析,要給飛行中的飛機換引擎,最重要的就是:

  • 得到產品方面的配合對需求做好鎖定;

  • 在開發過程中對新系統做好充分測試減少上線後修Bug的工作量;

  • 對遷移方案以及回滾方案做好自動化的指令碼以及充分的驗證。

安全意識十原則

對於安全我不是專家,但是我發現有這麼一個問題,那就是做業務開發的同學往往一點安全意識都沒有,如果有的公司沒有安全方面的部門或專家的話,那麼安全問題真的會很嚴重,外面所謂的一些安全公司的外包滲透服務往往是淺層次的機器做一下掃描和滲透,很少在程式碼和邏輯層面做深入的分析,安全要做好還是要靠一執行緒序員和產品經理的點點滴滴的意識。

在這裡,我介紹一下我總結出來的偏產品技術(網路層面還有很多工作需要做,這裡沒有涉及)方面的安全意識十原則(駭客白帽子肯定會有自己的一些成體系的方法論,我這裡更多的是根據之前踩過的坑自己總結的一些經驗):

1、安全問題是木桶效應。整個系統的安全程度取決於木桶最短的那塊板。很多時候我們會召集安全專家和架構師和主站主流程主域名的系統進行安全分析和滲透測試,而駭客知道這點也往往喜歡找邊緣化的子站點或非核心邏輯進行攻破,這些模組或站點往往是由初級程式設計師打造,有的甚至還不是主站統一的技術架構,總體上會防備薄弱。駭客能夠攻破任意站點進去到內網,就有種種可能。針對這點,我們需要做的是對於安全的排查,需要全面覆蓋,除非子站在部署上使用者體系上徹底隔離。

網際網路架構實踐:給飛機換引擎和安全意識十原則

2、開發層面:不要信任客戶端的任何東西。對於HTTP協議,不管是頭裡面的東西(來源、客戶端型別、Cookie)還是正文裡面的東西,任何資料都是可以偽造的。我們往往會覺得Get的東西暴露在瀏覽器地址上,裡面的引數不安全,Post過來的資料因為不暴露就安全,然後會信任Cookie中的資料做一些許可權控制,會用頭裡面的一些資料做一些安全性控制。這些不是說不能做,而是要從根子裡面有這個意識所有客戶端的東西可以用但不能不經過判斷直接相信。有一個容易犯的錯誤是,在設計Controller的時候我們可能會在引數裡讓客戶端傳過來UserID、Price等資訊。這裡的問題在於,登入後的UserID應該是儲存在服務端的,客戶是誰不是客戶自己說了算的,應該是我們根據SessionID在服務端獲得的,我們需要全面排查程式碼,不允許在Controller的方法裡存在類似於使用者ID這樣的欄位。

對於Price也是一樣的道理,如果這是一個購物的過程,那麼訂單的價格一定是在服務端計算的,我們只能依靠客戶端傳過來的ItemID來計算價格,不能相信和直接使用客戶端傳過來的價格入庫(僅僅作為呈現是可以的,客戶端來的還是呈現在客戶端,但是不能用於計算)。其實更麻煩的是,很多時候我們會用框架生成的Controller的CRUD介面的程式碼,框架會在引數內直接放上完整的Entity,然後程式碼裡會使用這個Entity直接對接資料訪問層入庫,因為客戶端和服務端用JSON在通訊,雖然我們看到客戶端傳給服務端的只是ItemID,但是你再傳一個Price確實也是可以生效進入Update語句入庫的(Entity某些欄位為空那麼就不會進入Update語句,不為空就會更新)。

下面會再提到這個問題,很多時候使用者看不到的不等於程式邏輯上不可行,服務端的介面層面往往會有比看到的更多的許可權(甚至可以由貫穿資料庫的完整CRUD Restful API)。

網際網路架構實踐:給飛機換引擎和安全意識十原則

3、開發層面:資料就是資料程式碼就是程式碼。不管是SQL隱碼攻擊也好XSS也好都是這個問題,把資料和程式碼混在了一起。對於客戶端過來的任何資訊應該都只是資料,應該很少會讓客戶端來告訴服務端執行的程式碼。所以這個事情我們要從兩方面來防範,第一客戶端過來的資料需要讓它當成資料來處理,不管是Encoding一下也好,SQL引數化(Mybatis的$和#問題)也好都是這個方面的措施(從前到後一路資料都以資料的身份在程式中流轉),第二從資料庫裡出來的東西也只能是資料不能讓它變為HTML或JS程式碼,也需要Encoding一次再在客戶端上呈現。SQL隱碼攻擊的防範很多人喜歡用敏感字元替換的方式來做,這種做法其實是會有遺留的,我們不應該去考慮排除資料中有程式碼的可能性,而是應該從根源上去讓資料只可能成為資料。

4、開發層面:使用者看不到不等於駭客看不到。第一個方面想說的是,隨著前後端的分離,現在很多請求都是AJAX請求,AJAX請求會有幾方面的安全疏漏:

  • 邏輯分散而明確。對於服務端渲染,我們會把各種邏輯整合在一起,使用者看到的是一個完整的渲染後的頁面不清楚裡面有多少邏輯,對於AJAX請求,我們會以良好的方法命名來命名各種API。這個時候會給駭客可乘之機,會更容易分析理解程式執行的流程,畢竟在尋找突破之前需要先理解程式的流程。

  • 容易覺得AJAX請求是前端程式發起的而忽略許可權問題。沒錯,AJAX的發起人也是我們的程式碼,但是AJAX請求也是HTTP請求誰都可以發起。如果我們對AJAX請求的引數設計有所鬆懈,犯了之前說的兩個錯的話那就很危險了。特別是前後端都是一個人來寫的話,程式設計師在實現程式碼邏輯的時候往往只會考慮資料的傳輸簡單通暢,後端給前端,前端再給後端放鬆安全意識。

網際網路架構實踐:給飛機換引擎和安全意識十原則

第二方面說的是,後端往往會返回更多的資料給前端,前端選擇需要的資料進行呈現,有的時候甚至直接返回的是程式碼生成器生成的DbEntity(在三層架構中,DbEntity對應表結構,ServiceEntity是充血的領域實體,ResponseEntity是面向呈現的實體,三者不應該是一套)。這裡的問題在於:

  • 服務端返回了過多的資料,這是比較危險的事情,因為有一些內部的欄位會返回出去。

  • 服務端返回的資料列對應了資料庫實際的列,相當於暴露了表結構,對於以後各種API的嘗試和注入極端危險。

  • 一些敏感的資料也直接返回給客戶端了,雖然客戶端不會展現出來,但是對於駭客來說根本不在於使用者看得到看不到這些資料。

這就要求我們在做設計的時候儘可能仔細審視AJAX的介面的許可權、資料開放性和脫敏等問題。一些框架提供的腳手架生成工具以及Restful API自動生成工具,即使要用也要做好許可權設定。

5、開發層面:最小化介面許可權設計複用性矛盾。在做設計的時候我們會考慮到複用性的問題讓方法儘可能通用,但是對於對外的介面我們要儘量收縮功能。舉一個之前看到的例子,我們需要驗證遊戲的密保卡,需要使用者告知三個座標的密保數字,比如A1B2C3三個座標,每一個數字是0到99,三個數字同時猜對的可能性是百萬分之一,但是介面的設計居然是直接讓使用者傳三個座標,這裡有兩個嚴重的設計錯誤:

  • 第一,為什麼是允許客戶端傳過來三個密保的位置,合理的做法是服務端告知客戶端此次校驗的ID,然後客戶端傳過來ID,服務端在Session或快取中去獲取座標位置。

  • 第二,介面允許傳三個座標也算了,還允許是相同的座標,我們完全可以改造介面傳A1A1A1然後依次暴力破解0到99,馬上就可以得出A1座標的值,幾秒的時間就可以把整個密保卡完全暴破出來。

在設計服務端介面的時候,我們最好針對某個功能設計最小的介面,而不是開放的通用的介面,對外的介面設計安全性需要大於重用性。

網際網路架構實踐:給飛機換引擎和安全意識十原則

6、開發層面:一開始就要考慮安全,放出去了就沒後悔藥。比較無奈的是,很多專案我們處於趕進度,一開始第一版的時候並沒有對介面做加密和簽名驗證。如果做的是一個APP,那麼我們要考慮到APP使用者不升級的情況,即使v2的版本的資料都是加密的,引數都是驗籤的,但是我們還不能下架v1版本(強更會損失多少使用者難以估計)。這個時候就比較無奈了,明知道v1版本的安全性有著很大的風險卻不能升級。不僅僅是介面的安全性,安卓客戶端的程式碼也要考慮到反編譯的可能性需要混淆。之前遇到過有一個App客戶端裡網路層直接使用了服務端的介面定義,導致客戶端程式碼裡可以對服務端所有介面一清二楚,還不乏一些不能對外使用的內部介面以及已經淘汰的老介面,加上介面的呼叫又沒有簽名資料傳輸又不加密,拿著這份網路協議什麼都可以幹。

7、產品層面:做好防刷和暴力破解控制。顯性的功能固然重要,但是產品經理也需要在隱性功能和風控策略上下一些功夫,產品經理沒有這方面意識的話開發往往更不太會做深入考慮。包括:

  • 公開出去的簡訊驗證碼服務防刷控制(防止被刷子利用做簡訊轟炸),在使用者體驗和防刷上做平衡,比如到達一定的頻次後出驗證碼,服務端對客戶端的一些頭做校驗(刷子一般不知道這樣的邏輯)等等。不僅僅是簡訊驗證碼,公開出去的不需要授權就可以用的服務都需要防刷。

  • 登入是使用者從匿名進入授權的重要環節,對這一環節做一些策略。比如異地登入提醒、錯誤登入後出驗證碼、太多次錯誤後禁用等等。

  • 涉及到積分、兌換、返現、抽獎這種和錢打交道的業務,要多考慮是否會有刷的可能,不僅僅是程式邏輯方面的漏洞,唯一性的判斷,還要考慮積分流通起來後是否會有擼羊毛小換大的可能。有利益的地方就有羊毛,如果一個業務都是羊毛在參與的話那麼賬面資料可能挺好看,但實際上幾乎帶來不了忠實的有留存的使用者。

重要的業務需要有風控方面的產品經理和業務產品經理一起來參與,共同討論流程,防羊毛,防駭客, 防刷子。

8、產品層面:注意產品邏輯一體性。很多業務流程是授權-執行這樣的兩步,但有些時候這兩步在產品設計的時候並不一定是同一個產品經理在設計,會出現授權和執行不一致的情況。見過一個修改繫結郵箱的產品邏輯,使用者在頁面X先進行簡訊驗證碼驗證,然後就可以跳轉到Y頁面進行修改繫結郵箱,X頁面是通用的簡訊驗證頁面,Y是新的修改繫結郵箱的需求,這裡出現一個邏輯漏洞,就是在X頁面驗證完成後進入Y頁面,如果這個時候利用另外一個TAB退登登入切換使用者後,在Y頁面還是可以修改繫結郵箱的,這個時候程式從Session中讀取的到使用者並不是之前那個透過簡訊驗證的使用者,而是後面登入的新使用者,相當於我們就可以為任意使用者修改繫結郵箱了。對於多步走的邏輯,我們一定要作為一個整體來思考。駭客在尋找突破的時候特別喜歡尋找ABC幾步走的邏輯,然後嘗試最後一步C是否可以單獨來做尋找邏輯方面的漏洞。

網際網路架構實踐:給飛機換引擎和安全意識十原則

9、運維層面:做好異常資料監控報警。在運營層面,我們需要對方方面面的資料有報表或監控,對於異常的資料徒增及時進行調查,業務量的增加如果不伴隨活動或推廣的話不一定是好事情。在運維層面,我們同樣也需要對網路磁碟的使用徒增以及服務的呼叫量上升查明原因,看看這是業務導致的還是可能是安全性問題。對於各種三方服務(比如簡訊通道、CDN、檔案儲存等)的使用,最好也有日報級別的監控,以免看到月度的賬單後再發現被刷的問題哭都來不及。

10、運維層面:對內的資料和許可權問題。我們說內部人是最難防的,內部系統繁雜,授權和審計做的可能不那麼完善,內部人員如果有心的話獲取一些資料做一些壞事造成的影響會比較大,我們需要從幾方面來做一些措施:

  • 線上的配置儘量加密,配置資訊和程式碼分離

  • 敏感資料(使用者資訊)在資料庫中加密儲存

  • 線上資料訪問需要使用類似於phpmyadmin的Web網站,不允許使用客戶端工具,這樣可以做比較健全的授權,防止資料匯出

  • 內部系統涉及到使用者資訊部分脫敏顯示,顯示完整的資料需要上級授權

  • 所有內部系統的登入和使用需要有完整的日誌,對日誌進行分析和定期的審計

網際網路架構實踐:給飛機換引擎和安全意識十原則

網際網路架構實踐:給飛機換引擎和安全意識十原則

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562044/viewspace-2285942/,如需轉載,請註明出處,否則將追究法律責任。

相關文章