# 9

我們都聽過太多的資安事件，那我們該如何思考跟防範呢?

我們的資料從一個點到另外一個點時，應該如何保護它們，也需要考慮在其他情況下如何進行保護。 我們需要考慮在底層操作系統&網路的安全。 有太多需要考慮，有太多可以做的事情。

那什麼是足夠安全呢?

## 身分驗證和授權

當談到與我們系統溝通的人或事時，**身分驗證** 和 **授權** 是核心概念。

通常來說，當我們抽象地討論 進行驗證的人或事時，我們稱之為 **主體(principal)**。

通常，當一個主體通過身分驗證後，我們將獲得關於它的信息，而這些信息可以幫助我們決定其可以進行的操作。 例如：登入後，我們知道他是哪個辦公室的，就可以根據權限決定他能做什麼、不能做什麼。

通常對單塊系統來說，應用程序本身會處理身分驗證和授權。但在分布式系統這個領域，我們需要考慮更高級的方案。 我們不希望每個人使用不同的 Account & password 去登入不同的系統。 我們希望能有一個單一的標識 & 只要進行一次驗證。

### 常見的單點登入實現

**SSO(Single Sign-On, 單點登錄)**

在企業級領域中佔據統治地位的 SAML & OpenID Connect，也提供了這方面的能力。 雖然術語略有不同，但它們或多或少使用了相同的核心概念。

當主體試圖訪問一個資源(web api, 資源提供者)時，會被導向到一個身分提供者進行身分驗證。 這個身分提供者 會要求 Account & password 進行驗證，它會發消息給剛剛的資源，讓服務提供者決定是否允許 主體 訪問資源。

**這個 身分提供者 可能是一個外部託管系統，也可能是自己組織內部的系統。** 例如：Google 提供了一個 OpenID Connect 的身分提供者。 不過對於企業來說，通常有自己的身分提供者，它會連接到公司的**目錄服務**。 目錄服務可能使用 LDAP(Lightweight Directory Access Protocol, 輕量級目錄訪問協議) 或 活動目錄(Active Directory)。 這些系統允許你存儲主體的信息，例如它們在組織中扮演什麼樣的角色。 通常情況下，目錄服務和身分提供者是同一個系統，但有時也會有所不同，但保持連接。 例如：Okta 是一個託管的 SAML 身分提供者，它可以處理 雙重身分驗證 這樣的任務，但可以連接到你公司的目錄服務，將其作為信息來源。

SAML 是一個基於 SOAP 的標準，盡管有庫和工具支持它，但用起來還是相當複雜。 基於 Google 和其他公司處理 SSO 的方式，OpenID Connect 已經成為 OAuth2.0 具體實現中的一個標準。 它使用簡單的 REST 調用，因為提高了其易用性，在我看來很有可能進軍企業級應用。 現在最大的障礙，是缺乏支持它的身分提供者。 對於面向公眾的網站而言，或許可以選擇 Google 作為提供者，但對於內部系統，或對於數據需要有更多控制權的系統而言，你會希望有自己的內部身分提供者。 除非等到現有的身分提供者開始支持 OpenID Connect，不然它的發展會僅限於 公共身分提供者 這種有限的情況。

## 單點登入網關

在微服務系統中，每個服務可以自己處理 如何重定向到 身分提供者，並與其進行握手。 顯然，這意味著大量的重複工作。 使用共享庫可以解決這個問題，但必須小心避免可能的耦合。而且倘若有多個不同的技術線，共享庫也很難提供幫助。

你可以使用位於 **服務** 和 **外部世界** 之間的 **網關** 作為代理，而不是讓每個服務 管理 與身分提供者握手。 基本想法是，我們可以集中處理 重定向用戶的行為，並且只在一個地方執行握手。

然而，我們依然需要解決下游服務如何接收主體的資訊，例如 Account & 角色。 如果你使用 HTTP，可以將這些信息放到 Header。 在這方面 Shibboleth 這樣的工具可以幫助你。作者曾看過有人將它和 Apache 一起使用，這種方式可以很好的處理 基於 SAML 的身分提供者的呼叫。

另一個問題，如果我們把身分認證的問題放在網關，由於希望我們的開發、測試環境可以類似生產環境，這就會成為一個挑戰 \~ 要如何確保開發人員不需要太多的工作，就可以啟動一個網關和背後的服務。

這種方法的最後一個問題，是網關造成的虛假安全感。 當我們把雞蛋都放在同一個籃子哩，依靠網關來處理每一步的安全措施。我們都知道，假如這個點發生故障 ... Boom!

要有深度防禦的理念，從網路邊界、子網、防火牆、主機、操作系統、底層硬體。你必須在所有這些地方都實現安全措施。

顯然我們還可以用網關來做其他事情，比如說運行其他軟體等等。 不過一定要小心，當網關承擔了越來越多的功能後，本身也會成為一個龐大的耦合點。而且功能越多，受攻擊的面就越大。

## 細粒度的授權

我們可以在網關提供相當有效的粗粒度身分驗證，然後在後續的微服務去做更細的主體權限檢查。

我們構建的軟體要與組織的工作方式相匹配，所以也盡量以這樣的方式去使用角色。

## 服務間的身分驗證和授權

上述的 **主體** 都是用來描述可以進行 **身分驗證** 和 **授權** 的任何事物，但剛剛的例子都是使用電腦的人類。 那如果 程式 or 其她服務 之間該如何進行身分驗證呢?

### 在邊界內允許一切

第一種選項：**在邊界內對服務的任何呼叫都是默認可信的。**

這取決於數據的敏感性，這種方式可能沒有問題。

一些組織會嘗試在網路範圍內確保安全，因此認為兩個服務之間彼此訪問，不需要額外做任何事情。 然後，倘若有攻擊者入侵到網路中，這將會對中間人攻擊沒有任何防備。

如果攻擊者決定 攔截、讀取、修改 正在發送的資料，我們完全不會知道，甚至假裝我們正在通信的另一方。

大部分的組織僅僅使用 HTTPS，這並不是一件好事，並沒有意識到他的風險。

### HTTP(S)基本身分驗證

HTTP 基本身分驗證，允許客戶端在標準的 HTTP Header 中發送 userName & password。 服務端可以驗證後，確認客戶端是否有權訪問服務。

由於任何中間方都可以看到 HTTP Header 的資訊，所以在做基本身分驗證的時候，應該使用 HTTPS。

當使用 HTTPS 時，客戶端可以確信 它所通信的服務端就是客戶端想要通信的服務端。 它可以讓我們避免人們竊聽 Client 和 Server 之間的通信，當然也就不會被竄改。

Server 端要管理自己的 SSL 證書，當有多台主機需要管理時會出現問題。 管理這方面的自動化工具還不成熟，而且自簽名證書不容易撤銷，因此需要對災難情景有更多的考慮。 看看你是否能避免自簽名，用來避開這些工作。

SSL 上的流量不能被 反向代理服務器 (比如 Varnish 或 Squid) 所暫存，這是使用 HTTPS 的另外一個缺點。 這意味著如果想要暫存，就必須實作在 Server or Client。 有一種方法是：在負載平衡中 把 HTTPS 的請求 轉換成 HTTP 的請求，然後在負載平衡中 就可以使用暫存了。

如果我們已經在使用 SSO 方案 (比如包含用戶名密碼信息的 SAML)，我們想要基本身分驗證 使用 同一套認證資訊，然後在同一個 process 頒發&撤銷 嗎? 讓服務與實現 SSO 所使用的目錄 進行通信 即可做到這一點。 或許我們可以在服務內部儲存帳密，但需要承擔 存在重複行為 的風險。

### 使用 SAML 或 OpenID Connect

如果你已經使用 SAML 或 OpenID Connect 來作為身分驗證和授權方案，你可以在服務之間的溝通也使用它們。

如果正在使用一個網關，可以使用這個網關去路由所有內網通信。 但如果每個服務自己處理呼叫，那麼系統應該就這麼工作。 好處在於，利用現有的基礎建設，並把所有服務的訪問控制集中在 中央目錄服務器。 而想要避免中間人攻擊，我們仍然需要通過 HTTPS 來路由通信。

客戶端有一組憑證，用來向身分提供者驗證自身，而服務獲取所需的信息，用於任何細粒度的身分驗證。

這意味你需要為客戶端創建帳戶，有時被稱為 **服務帳戶**。 許多組織普遍使用這種方法。 建議：如果打算創建服務帳戶，應盡量限制使用範圍。考慮每個微服務都要有自己的一組憑證。這樣如果憑證被洩露，就只要把有限的、受影響的憑證撤掉就好，這讓 撤銷/更改 更簡單。

### 客戶端證書

另一種確認客戶端身分的方法是使用 TLS (Transport Layer Security, 安全傳輸層協議)，TLS 是 SSL 在客戶端證書的繼任者。 在這裡，每個 Client 都安裝了一個 X.509 證書，用於 Client 和 Server 之間建立通信鏈路。 Server 可以驗證 Client 證書的真實性，為客戶端的有效性提供強而有力的保證。

使用這種方法所需的工作更加繁重，除了創建和管理數量更多的證書，更麻煩的在於證書本身，你可能花費大量時間去弄明白 一個你認為應該安全有效的證書 為何 Server 不接受。而在最壞的狀況，可能還要考慮撤銷和補發的難度。

這些額外的負擔代表，當你特別關注所發數據的敏感性、重要性、或無法控制發送數據所使用的網路時，才考慮使用這種技術。 因此，只有非常重要的數據時，才需要安全通信。

### HTTP之上的HMAC

正如我們前面所討論的，如果擔心帳號密碼被洩露，使用普通 HTTP 並不是非常明智的。傳統的替代方式是使用 HTTPS 路由通信，但也有一些缺點。除了需要管理證書，HTTPS 通信的開銷使得服務器壓力增加(盡管已經比幾年前小得多)，而且通信難以被輕鬆的暫存。

而另外一種使用 HMAC (Hash-based Message Authentication Code, 基於Hash 的消息碼) 對請求進行簽名，它是 OAuth 規範的一部分，並被廣泛應用於亞馬遜 AWS 的 S3 API。

使用 HMAC，request body 會用 private key 去做 Hash，生成的 hash value 會隨著 request 一起發送。 server 會將 request 和自己的 key 去做 hash，看兩個 hash 是否匹配。

這種方法只能確認 request body 是否有被竄改，但無法避免 request 暴露給第三者。 並且要確保，這個 private key 不會洩漏給第三者知道。

HMAC 這只是一種模式，並不是標準，所以有各種的實現方式 \~

可以參考亞馬遜的 S3，SHA-256，JWT(JSON Web Tokens, JSON Web 令牌, [http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)) 也值得一看。

### API密鑰

像 Twitter、Google、AWS 這樣的服務商，提供的所有公共API 都使用 API 密鑰。 API 密鑰 允許服務識別出是誰在呼叫，然後對它們進行限制。 限制通常不僅限對資源的訪問，還可以擴展到 **對使用者的限速**，用以保護對其他使用者的服務品質。

具體該如何使用 API密鑰 來處理微服務間的訪問，取決於妳使用的技術。

除了上面的 HMAC，更常見的一種方法是，使用 公私鑰對。 通常情況下，正如集中管理 人的身分標識 一樣，我們也會 集中管理密鑰。 網關模型 在這個領域很受歡迎。

API 密鑰 重點關注於 對程式的易用性。 相對於處理 SAML 握手，基於 API密鑰 的身分驗證更加簡單。

API密鑰 的解決方案 在商業、開源領域 存在很多選項，有些產品只處理 API密鑰交換和一些基本的密鑰管理。 其他工具提供 包括 限速、變現、API目錄和發現系統 等功能。

一些API系統 允許你將API密鑰 和 現有目錄服務聯繫起來。 這允許將 API密鑰 發布給組織中的主體(人 or 系統)，從而想管理憑證一樣，去管理這些密鑰的生命週期。 這樣的作法，讓我們有機會通過不同的方式去訪問，但都是用同一組密鑰。

### 代理問題
