首頁 > 科技

提高(微)服務安全的非完全攻略

2021-07-28 03:13:02

【編者按】千里之堤毀於蟻穴。服務安全有點類似於機場安檢。工作人員會檢查每一個人,禁止他們帶某些東西上飛機等等,儘管99.9999%的人都不會劫持飛機。而機場採取的所有措施針對的都是0.0001%的情況。因為後果影響非常大。

作者 | Madalin Ilie

譯者 | 彎月

軟體的安全問題既困難又複雜。許多人認為安全屬於正常開發流程之外的東西。通常被視為某些安全人員的責任,他們只需要關注安全,不需要理解我們在複雜的微服務、事件驅動、API優先戰略以及雲生態系統中快速交付的業務價值。我認為正是由於這種思想,我們很難在早期階段思考安全問題,找出任何有可能破壞系統的人或事。安全導致複雜的壞境又多了一層複雜。安全不僅僅體現了現代架構的技術複雜性,而且還有很多其他方面的問題,比如快速走向市場、嚴格的期限、團隊內部問題、效能問題、流程太多、會議太多等等。雖然安全非常複雜,但是它並不會在某一天裡突然破壞你的系統。可能在經過數月/數年後,才會有人發現漏洞。那麼,為什麼我們要從第一天起就關注安全呢?話雖沒錯,第一天就出現安全問題的可能性非常小。我們都希望集中精力完成具有直接價值的功能,而不是預防未來可能出現的問題。然而,關鍵在於,安全問題可能會導致整個系統癱瘓。這對於你和你的使用者都非常不利。

那麼,如何在不過度設計安全性和偏執於一切之間取得平衡,同時仍然還能專注於業務價值呢?這其實是一種思維模式,而不是一個單獨的關注點。我並不是說每個人都需要成為安全專家以及無所不知。我是說人們應該開發安全軟體,就像他們開發軟體一樣。通過一種方式將引入漏洞的可能性降到最低。

灌輸這種思維模式的最佳方式是採用一套標準和實踐來培養習慣。繼續拿機場安檢做類比,你不能讓安檢人員自行做決定:

  • 「這個人看上去是好人,他的手提行李可以攜帶剪刀和刀。」

  • 「這位先生看上去嚴重脫水,他可以攜帶一大瓶液體上飛機!」

你需要創建一套平等且適用於每個人的規則和程式(即標準)。此外,你還需要制定一套關於如何處理特定情況的指導方針(即實踐):如果你發現手提行李中有可疑物品,則可以單獨檢查。

接下來,我將詳細介紹涵蓋整個系統發展生命週期的標準和實踐。雖然這個列表無法做到詳盡(對於有些小節的內容,你可能有更多良好的實踐),但我希望能夠幫助你思考一些不太常見的情況。

安全的重點

我簡單地將安全問題分為了兩個主要領域:

  • 基礎設施安全:與應用程式在生產中的部署和運行相關的安全問題。

  • 應用程式安全性:與應用程式的實現以及業務背景細節相關的安全問題。

關於如何解決這兩個問題,有很多資源:

  • PCI 安全要求(https://www.pcisecuritystandards.org/):側重於財務軟體,但也可作為其他系統的最佳實踐。

  • 安全程式碼(https://safecode.org/category/resource-publications/):有關開發、測試、架構以及開發運維的通用實踐。

  • 有關安全程式設計的十大最佳實踐(https://wiki.sei.cmu.edu/confluence/display/seccode/Top+10+Secure+Coding+Practices):側重於某些程式語言的安全程式設計最佳實踐,也有一些通用的實踐。

  • OWASP 備忘單系列(https://cheatsheetseries.owasp.org/index.html):覆蓋了大多數軟體開發領域的備忘單。

上述資源非常全面,包括許多解決系統發展生命週期內有關安全問題的細節和實踐。每位開發人員都應該定期做檢查,並掌握最新資訊。但在實踐中,我們很難做到。下面,我將設法總結出我們需要思考的一些重要問題(注意:這些實踐與業務領域無關)。雖然這不是一份詳盡的清單,也不是靈丹妙藥,但它可以幫助你建立堅實的基礎,將發生安全問題的可能性降到最低。

解決安全問題

基礎設施的安全比較容易解決,因為很多人都會使用產品或雲服務。這些產品和服務已經包含了非常好的安全功能,如果你使用Web應用程式防火牆,則可以相信該產品能夠保障安全,不需要由你來實現安全。我並不是說這樣做會更容易,而是說你有更多控制。

應用程式的安全則更加難以預測。你需要依靠開發人員的技術來實現安全。你需要確保他們不會做愚蠢的事情,比如在原始檔中儲存明文密碼。

以下是我認為能夠幫助你建立安全思維方式的實踐列表,主要面向常規開發人員。這裡的「常規」指的是實際實現程式碼的人,而不是其他專注於設計、規劃或管理的人。這些規則關注的都是構建 (REST) API 時的應用程式安全。雖然表面看起來它們與安全並沒有直接的聯絡,但最終它們都可以減少引入安全問題的可能性。

注意:本文的大多數示例都使用了Java。

標準

如上所述,標準的使用是建立思維方式的主要機制。所有項目都應該採用同一套標準。並非所有人都喜歡標準,有些人可能會認為標準限制了人們的選擇和創造力。但我認為,這是一種簡單的獲得統一性的方法,尤其是當多個團隊在同一平臺上工作時。建立標準之後,新成員可以更輕鬆地加入項目,而且還能降低引入bug、不一致或在某些愚蠢的事情(比如空格還是製表符)上發生爭執的可能性。我們可以將這些時間節省出來討論更有意義的問題。標準不一定要涉及很多細節,大多數標準應該說明建立在現有實踐之上的原則和選擇。

文件

需要考慮的關鍵事項:

  • 將程式碼的介面以及API合同寫在文件中;

  • 定義文件策略:

  • 整體的文件策略是什麼?

  • 項目的 README.md 檔案中包含什麼?

  • 是否需要更新更廣泛的文件?

  • 採用哪些畫圖工具?

  • 是否使用輕量級架構決策記錄?

  • 文件應當儲存在Git程式碼庫中?還是採用單獨的工具?

  • 如果儲存在項目中,推薦的資料夾結構是什麼?

通用(微)服務設計指南

需要考慮的關鍵事項:

  • 使用藍圖/模板/原型作為所有(微)服務的起點;

  • 將藍圖與所有公共庫、插件等捆綁在一起,並且必須符合標準;

  • 每個(微)服務必須能夠通過運行一個命令來啟動;

  • (微)服務只能通過API/事件處理資料,沒有後門;

  • 每個(微)服務都是獨立的;

  • 所有(微)服務都符合應用的十二要素,以及其他標準。

程式碼格式/風格

選擇一種程式碼風格,並貫徹到底。如果可以,在提交前自動格式化。

命名約定

選擇一種約定,並貫徹到底。

API標準

需要考慮的關鍵事項:

  • 遵循 REST 命名實踐(名詞、複數以及其他常見的標準),網上有很多指南,選擇一種,並貫徹到底;

  • 與命名規則統一。該標準適用於一切,比如負載物件命名、屬性等,而不僅限於端點。無論是駝峰式、蛇式還是烤肉串式,選擇一種,並貫徹到底;

  • POST、PUT、PATCH的響應包含有意義的主體;

  • 使用有意義的 HTTP 狀態碼,不要遇到錯誤就返回 400;

  • 所有端點必須返回有意義的錯誤情況;

  • 提供錯誤列表(更多細節請參見錯誤處理部分的介紹);

  • 考慮 OpenAPI,以及契約優先開發,即剛開始的時候編寫 OpenAPI 契約,與(內部)消費者溝通,這樣可以實現更好的並行開發;

  • 使用有意義的描述和示例記錄 OpenAPI 契約;

  • 所有(內部)API 必須使用 CorrelationId/TraceId 的標頭;

  • 預設情況下,所有 API 輸入都必須有非常嚴格的限制;

  • 所有 API(內部或外部)都必須經過身份驗證,最好採用實時授權;

  • 所有 API 必須重用相同的公共資料結構:可以採用通用的地址、人、國家等,也可以定義特定於業務的資料結構;

  • 所有 API(內部或外部)只能通過 HTTPS 公開;

  • 考慮在 API 的響應中返回安全標頭,例如:Cache-Control: no-store,Content-Security-Policy: frame-ancestors 'none',Content-Type, Strict-Transport-Security,X-Content-Type-Options: nosniff,X-Frame-Options: DENY等。

  • 內部 API 之間的通訊不應該通過網際網路(除非是架構有意為之,或有此類要求);

  • 不要通過網際網路公開管理端點;如果有這種要求,請使用身份驗證;

  • 確保所有 API 都會嚴格驗證收到的請求:不允許使用未記錄的 JSON 欄位、拒絕格式錯誤的 JSON 等;

  • 正確使用資料類型,不要將所有內容都作為字元串;

  • 儘可能使用列舉值;

  • 定義字元串的長度限制,定義數字的最小值/最大值;

  • 定義每個字元串的輸入限制模式;

  • 某些屬性有明確的定義,因此定義正則表示式很容易,比如國家程式碼一律以「[A-Z]+」開頭。但有些屬性很難定義正則表示式,比如考慮到所有語言中人們的姓氏,這個屬性(lastName)的要求就會非常寬鬆。建議定義一些能夠防止出現奇怪字元的模式,比如 Unicode 控制字元、分隔符或符號;推薦的正則表示式物件如下:「^[^p{C}p{Z}p{So}]*[^p{C}p{so}]+[^p{C}p{Z}p{So}]*$」,但這並不能確保程式可以免受任何類型的注射,你仍然需要很好地理解資料的去向和處理方式,但至少可以確保表情符號不會破壞你的系統。

標準日誌

需要考慮的關鍵事項:

  • 日誌的格式:使用逗號分隔的鍵值對(key=value)?還是使用JSON物件?選擇適合工具的格式;

  • 日誌的每一行都包含 CorrelationId/TraceId,這樣可以方便工具創建儀表板;

  • 日誌中記錄方便理解實際情況的資訊:哪個實體?業務領域?成功了嗎?失敗了?

  • 一些參考做法(https://www.javacodegeeks.com/2011/01/10-tips-proper-application-logging.html);

  • 在實際的日誌實現之上使用抽象層;例如在 Java中,使用由logback實現的slf4j;

  • 將日誌作為橫切關注點,利用面向切面的程式設計,僅在異常情況下記錄日誌,限制敏感內容的記錄;

  • 不能將日誌作為記錄一切備用資訊的手段,將所有請求和響應都完整地記錄下來。應當謹慎地記錄日誌,即使在偵錯級別或較低級別中也應如此。

  • 更多有關日誌記錄的參考資訊(https://ludovicianul.github.io/2021/07/06/incomplete-list-of-security/)。

資料標準

需要考慮的關鍵事項:

  • 使用現有的 ISO 標準處理廣為人知的物件:貨幣、日期、金額等;

  • 定義可重複使用的特定於業務的物件;

  • API物件、資料庫實體和事件也需要採用這些標準。

處理資料

需要考慮的關鍵事項:

  • 在處理資料之前清理資料,下面是一個通用的清理正則表示式:「^[^p{C}p{Z}p{So}]*[^p{C}p{so}]+[^p{C}p{Z}p{So}]*$」,雖然無法杜絕所有的問題,但是可以刪除可能會導致系統崩潰的奇怪字元;

  • 確保不會將輸入資料傳輸到內部的訪問操作,如資料庫查詢、命令列執行等。使用參數化的資料庫查詢,具體指定獲取的內容與傳遞的內容;

  • 當需要針對特定輸入的處理施加限制時,請使用白名單(而不是黑名單);

  • 採用防禦性程式設計實踐;

  • 確保使用不易受到 XXE 或類似攻擊的高效 XML 解析器。最好不要使用 XML 作為輸入,除非上下文有這樣的強制要求。

日誌資料

需要考慮的關鍵事項:

  • 不要記錄敏感資料。如果出於某種原因仍然需要記錄,請遮蔽敏感資料。這裡的敏感程度取決於具體的業務與法規;

  • 創建/使用一個庫,預設遮蔽平臺中最敏感的資料,例如,如果你正在處理付款,則預設遮蔽銀行卡號,不要讓每個開發者自行決定;

  • 考慮在每次新增新的敏感資料時擴展該庫,如果需要新增大量資料,則必須平衡效能;

  • 日誌記錄庫必須允許特定的配置,以便每個單獨的服務可以遮蔽額外的資料,而無需擴展該庫;

  • 日誌記錄庫必須提供清理方法(即通過呼叫特定的方法),確保所有情況都可以使用同一種清理技術;

  • 日誌記錄庫必須在記錄資料之前清理資料(比如刪除所有與「p{Z}p{C}p{So}」相匹配的字元);

  • 日誌記錄庫必須刪除 CR 和 LF 字元,以防止 CRLF 注入;

  • 建立明確的日誌歸檔策略。

儲存資料

需要考慮的關鍵事項:

只能儲存與當前上下文或可預見的將來相關的資料,資料儲存不能作為以備不時之需的手段;

  • 資料的儲存必須遵守相關規定,你必須知道這一點;

  • 某些資料不能明文儲存(比如信用卡號),可以使用硬體或軟體加密;

  • 不能在純文字檔案的版本控制中儲存隱私資料(密碼、加密金鑰、ssh 金鑰、私鑰等),必須使用專用產品或服務,例如 Vault、HSM等;

  • 在執行敏感資料的加密或雜湊操作時,必須「加鹽」和/或「加胡椒」,以防止暴力攻擊;

  • 考慮構建(或使用)令牌化敏感資料的集中式服務;

  • 令牌化所有受某種監管的資料:銀行卡號、PII 資料等。所有(微)服務都應該使用令牌(而不是實際的資料),並僅在需要時還原令牌,這可以最大限度地減少合規所需的工作,還可以更好地控制資料;

  • 加強令牌化解決方案的安全性,不允許從外部訪問其 API。

事件/訊息標準

需要考慮的關鍵事項:

  • 創建一個事件列表,讓每個人都知道每個事件的目的;

  • 使用事件規範進行驗證;

  • 避免使用通用事件來發送一切訊息,避免在不知情的情況下洩露敏感資訊;

  • 考慮用交換令牌代替包含敏感資訊的實際資料。

配置管理

需要考慮的關鍵事項:

  • 避免在原始檔中硬編碼配置;

  • 考慮使用集中式配置管理;

  • 按環境分隔配置;

  • 不要在原始檔或版本控制中儲存隱私資料(密碼、api 金鑰、ssh 金鑰、私鑰等),使用適當的隱私資料系統;

  • 不要保留任何可部署單元(雲服務、產品或自己的(微)服務)的預設憑據;

  • 不要將僅用於測試的程式碼或配置放入生產;

  • 不要只在服務中構建測試後門;

  • 使用版本控制來記錄配置更改;

  • 使用適當的配置完整性檢查機制。

錯誤處理

需要考慮的關鍵事項:

將異常和錯誤視為橫切關注點,利用面向切面的程式設計,使用 ControllerAdvices 等類似的工具;

將常見的異常/錯誤(驗證問題、未找到資源、格式錯誤訊息)處理邏輯放入到共享庫中,這可以減少(微)服務之間的互動摩擦;

編寫一個錯誤列表;

  • 使用錯誤程式碼(例如 MICRO-4221代表結構驗證導致請求出錯,MICRO-4222 代表業務驗證導致請求出錯);

  • 不要在響應中洩漏內部狀態,避免傳遞 e.getMessage(),返回的每個錯誤都必須由其根本原因創建,同時不要洩漏內部資料;

  • 使用 catch-all 機制,以避免因意外異常而洩漏內部狀態。你可以利用全局錯誤處理程式捕獲異常並返回 500;

  • 所有錯誤都應該返回同一個物件,以實現統一的體驗;

  • 在API文件中使用適當的 HTTP 狀態碼記錄所有的錯誤情況。如果使用 OpenAPI,請記錄所有可能的 HTTP 狀態碼,即使它們返回相同的 OpenAPI 物件。

分支策略與提交

需要考慮的關鍵事項:

  • 使用簡單的分支策略,基於trunk的方式、GitHub工作流等,隨便選一個;

  • 儲存庫和分支採用有意義的命名;

  • 使用描述性提交,以方便將來追蹤變更;

  • 分多次提交少量的程式碼,以更好地分隔變更;

  • 使用智慧提交,即提供指向任務(來自任務管理系統)的連結;

  • 考慮使用預提交鉤子來驗證提交;

  • 提交訊息中不要包含敏感資訊;

  • 儲存庫啟用遠端訪問時需要特別小心,特別是託管在雲中的儲存庫。

程式碼審查

需要考慮的關鍵事項:

  • 必須執行程式碼審查(保持善意和自信,提供具體說明,以及其他良好的習慣);

  • 機械的檢查交給工具來做,專心審查功能以及是否符合標準和實踐;

  • 如果你反覆遇到同一個問題,請將其新增到標準中;

  • 建立檢查列表,直到所有人都養成關注同一個問題的習慣。

工具與第三方庫

需要考慮的關鍵事項:

  • 制定引入新工具的流程。進行權衡分析,並推廣到更廣泛的群體,得到所有人的接受/同意,並確保覆蓋更廣泛的情況;

  • 選擇開源軟體時注意許可;

  • 創建許可列表,在其中列出所有無需詢問即可使用的許可、需要討論的許可以及不允許使用的許可;

  • 在發現新工具/庫/產品時,不要著急使用,你需要考慮:是否穩定?是否有良好的維護?是否有成功案例?

  • 考慮使用 OWASP Dependency Check、License Plugin 之類的工具,或其他更復雜的工具,例如 Black Duck等;

  • 制定一個普遍認可的工具/庫創建列表,供所有人挑選;

  • 定期更新依賴項。

程式碼分析

  • 需要考慮的關鍵事項:

  • 使用一種或多種工具來分析程式碼;

  • 必須擁有一種(至少)常見的程式碼分析工具,以及一種注重安全實踐的工具;

  • 常見的(Java)程式碼分析工具:Sonarqube、PMD、SpotBugs;

  • 常見的安全程式碼分析工具:Veracode、Checkmarx、Sonarqube;

  • 你不需要遵循這些工具提供的所有標準規則(儘管通常這些標準都與行業建議一致),可以根據實際情況選出適合自己的規則。

測試

需要考慮的關鍵事項:

  • 執行各個級別的自動化測試:單元測試、整合測試、元件測試、API測試、端到端測試等;

  • 除了正面測試之外,還需要關注負面測試以及邊界測試,CATS 是一款優秀的 API 測試工具;

  • 不要忽視失敗的測試,即使是間歇性失敗的測試,其中可能隱藏了嚴重的潛在問題;

  • 測試必須具有彈性,而且必須充分;

  • 測試必須使用類似且可預測的方法;

  • 測試不得依賴於複雜的外部設定,必須能夠通過模擬依賴項、使用記憶體設定來完成測試,或採用測試容器,或僅依賴於已部署的(微)服務。任何其他步驟只會使設定更加複雜化;

  • 考慮在管道內新增一些安全測試;

  • 考慮突變測試。

CI/CD

需要考慮的關鍵事項:

  • 在重要的環節新增質量檢測,並作為檢查點,如果檢測失敗,則構建也會失敗;

  • 質量檢測必須符合這些標準,並自動檢查每個(微)服務;

  • 下面是一個 CI/CD 管道示例:

編譯和構建;

檢查格式;

運行測試並檢查覆蓋率;

運行突變測試;

運行程式碼分析;

運行安全程式碼分析;

檢查第三方庫的漏洞;

檢查第三方庫的許可;

部署;

運行 API 測試;

運行其他類型的測試。

  • 雖然看上去很冗長,但在微服務上運行這些檢查非常快;

  • 編寫自己的流水線指令碼;

  • 不要將流水線與(微)服務融合到一起;

  • 使用適合於所有(微)服務的流水線模板。

認證和授權

需要考慮的關鍵事項:

  • 不要自行構建身份驗證和授權,請使用標準的產品和服務;

  • 所有內部與外部API都需要身份認證,選擇一些可靠的認證服務;

  • 外部和內部呼叫需要使用單獨的身份驗證和授權機制:使用一組憑據/機制來驗證外部呼叫,再使用另一組單獨的憑據/機制來驗證內部呼叫;

  • 憑據始終需要加密,無論是否投入了使用;

  • 所有API(無論內部還是外部)都需要使用 HTTPS;

  • 不要通過 HTTP GET 接收身份驗證憑據,僅使用 HTTP 頭部或 HTTP POST/PUT;

  • 即使在偵錯時也不能記錄憑據,通過日誌記錄的catch all來避免記錄憑據;

  • 確保授權和身份驗證機制允許細粒度的控制和管理,即可以限制每個操作的呼叫次數、撤銷訪問、頒發額外的憑據等;

  • 考慮使用集中式身份提供程式和公共庫;

  • 對於高度敏感的 API/服務使用增強的安全控制(API 的雙向 TLS,服務訪問的 MFA);

  • 使用隨機數來防止重放攻擊;

  • 設計和構建採用最低特權原則。

通用安全實踐

需要考慮的關鍵事項:

  • 永遠不要使用自己的加密程式,不要在這個空間內重新發明輪子;

  • 使用行業推薦演算法:AES 256、RSA 2048+、SHA-2 512等;

  • 使用 TLS 1.3+ 來保證傳輸安全;

  • 在執行敏感資料的加密或雜湊操作時,必須「加鹽」和/或「加胡椒」,以防止暴力攻擊;

  • 檢查程式語言處理敏感資訊的實踐,例如在 Java 中,在處理密碼、銀行卡號、社會保險號時,必須使用 byte[],不能使用 String。必須儘量減少資料在記憶體中停留的時間並在使用後清除物件。

質量

如上所述,系統生命週期的標準和實踐並不一定會直接關係到安全。質量方面也有這個問題。當前的設計和方法缺陷可能會導致應用程式宕機,即便並不是由真正的安全問題引起的。

關於效能,需要考慮的關鍵事項:

  • 對於昂貴的資源,比如資料庫、API等,應該使用連線池;

  • 使用執行緒池;

  • 使用快取;

  • 操作資料時使用適當的集合;

  • 如果可以,請使用並行程式設計;

  • 確保你瞭解物件關係對映(ORM)如何生成查詢;

  • 避免將大量資源載入到記憶體,請使用資料流;

  • 建立每個(微)服務例項的效能基準,幫助你瞭解何時需要擴展;

  • 定期執行負載測試與效能測試。

關於彈性,需要考慮的關鍵事項:

  • 使用斷路器、重試、超時、速率限制;

  • 當依賴的 API 不可用時有明確的回退策略;

  • 關於彈性的資源:

https://engineering.grab.com/designing-resilient-systems-part-1

https://engineering.grab.com/designing-resilient-systems-part-2

  • 所有 API 都必須具備冪等性;

  • 不要在(微)服務例項中儲存狀態,可以考慮使用分散式快取。

關於可用性和可擴展性,需要考慮的關鍵事項:

  • 設計不應當限制(微)服務的水平擴展;

  • 做好故障計劃,建立自動化的機制,根據負載水平自動伸縮;

  • 考慮分片,只讀副本;

  • 使用多區域部署。

關於可觀察性和監控,需要考慮的關鍵事項:

  • 所有(微)服務都必須公開有關應用程式以及底層容器的健康端點;

  • 健康端點必須返回有關其所有依賴項的資訊:資料庫、加密服務、連線的 API、事件匯流排等。

  • 利用標準化日誌創建有意義的操作儀表板。

自動化

一切都應儘可能實現自動化。自動化可以確保預測性和一致性。應該利用 CI/CD 流水線自動化所有檢查,這些檢查將從質量的角度評估(微)服務。對於不適合自動化的標準,可以考慮 Semgrep 等工具。

總結

在本文中,我儘可能地列出了方方面面的實踐建議。雖然這不是一份詳盡的攻略,但可以作為建立安全思維模式的起點。在貫徹完成這些實踐後,你可以進一步深入的研究。這些實踐不僅可以確保系統安全,而且還具備結構化與統一性。在快速開發系統(無論是開發全新的系統還是改造遺留系統)時,這些實踐尤為重要。你不需要從第一天開始就貫徹所有實踐,特別是過多的通用標準會限制你的選擇。你可以逐步嘗試,然後看看效果。

原文連結:https://ludovicianul.github.io/2021/07/06/incomplete-list-of-security/

聲明:本文由CSDN翻譯,轉載請註明來源。


IT145.com E-mail:sddin#qq.com