在開發系統時,你是否曾經遇過這樣的錯誤訊息?
System.Security.Authentication.AuthenticationException:
The remote certificate is invalid because of errors in the certificate chain: Untrusted Root
或是:
System.Net.Http.HttpRequestException:
The SSL connection could not be established.
這些錯誤常與「憑證驗證失敗」有關,但一個常見的問題是:為什麼要由「我們」的程式碼來驗證對方的憑證?尤其當我們只是想呼叫一個 API、或透過 Proxy 發送請求,憑證不是應該在對方伺服器或 Proxy 設好就好嗎?
本文就來解開這個疑惑,從 HTTPS 原理談起,一步步帶你了解憑證驗證為什麼是「我們」的責任。
🐻HTTPS 是怎麼確保安全的?
HTTPS(HTTP over SSL/TLS)本質上是加密版的 HTTP。為了防止中間人攻擊、確保資料傳輸的完整性與機密性,它在建立連線時會進行一連串的「TLS 握手(handshake)」。其中一個關鍵步驟就是:
伺服器會將自己的憑證(certificate)提供給客戶端進行驗證。
🐻驗證什麼?
- 憑證是否由受信任的憑證機構簽發(CA)
- 憑證的有效期間是否正確
- 憑證的主體是否與目標主機(domain)相符
- 憑證鏈(Certificate Chain)是否完整與可信
這些檢查,都是在「客戶端」端進行的,也就是我們的程式碼端。
如果以上條件有一項不符合,就會產生憑證錯誤,導致連線中斷。
為什麼是我們驗證?
在 TLS 設計中,信任是「由下往上」建立的。舉個例子:
- 當你打開瀏覽器前往
https://example.com
,瀏覽器會先檢查該網站的憑證是否可信。 - 瀏覽器信任哪些根憑證(Root CA),是由你電腦或作業系統事先定義好的。
- 如果這條信任鏈斷了,連線就會被視為不安全、甚至被拒絕。
同樣地,當我們用程式(例如 C# 的 HttpClient
)去呼叫某個 HTTPS API,我們就扮演了「瀏覽器」的角色。我們的應用程式必須自己來驗證對方憑證是否安全可信。這樣做的目的是為了避免與偽造或遭竄改的伺服器通訊,防止中間人攻擊。文章最後會加碼說明:中間人攻擊是什麼?
那 Proxy 呢?不是中間人嗎?
Proxy(代理伺服器)如果是正向代理(Forward Proxy),它只是在你和目標伺服器之間轉發請求,TLS 仍是由你和目標伺服器建立,所以你仍然要驗證最終伺服器的憑證。
但如果是中介代理(如攔截型 Proxy 或防火牆),它可能會攔截你的 HTTPS 請求,然後自己與目標伺服器建立連線,再向你提供一份由它自己簽發的憑證。這就會導致憑證鏈出現「Untrusted Root」錯誤。
The remote certificate is invalid because of errors in the certificate chain: Untrusted Root
這表示中間那台 Proxy 給了你一張它自己簽發的憑證,但你的程式或系統並不信任這個簽發者。
🐻解法與實務做法
正確方式
將 Proxy 或公司內部憑證簽發單位的 Root CA 安裝到受信任根憑證存儲區中
- Windows:使用
mmc
加載「受信任的根憑證授權機構」 - Linux:加入到
/etc/ssl/certs
並執行update-ca-certificates
危險但常見的方式
在 C# 中,你可能會看到這段程式碼:
HttpClientHandler handler = new()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
或者:
HttpClientHandler handler = new()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
這都會完全忽略憑證驗證,通常用來「臨時繞過」開發環境的錯誤。但千萬不要在正式環境中使用,這等於你關掉了 HTTPS 的保護網!
🐻結論
信任是由客戶端建立的
TLS 的精神就是:由連線發起端(我們)決定信任誰。所以不是對方設定錯了,而是我們沒有準備好信任它的憑證。
當你遇到憑證錯誤時,不妨先問問自己:
- 這張憑證是誰發的?
- 我們的程式或伺服器信任這個憑證簽發者嗎?
- 是否有中間代理設備攔截了原始連線?
了解憑證驗證的責任歸屬,將幫助你更有信心處理各種 HTTPS 問題。
🐻加碼
中間人攻擊是什麼?
想像你坐在咖啡廳,透過 Wi-Fi 連上某個 HTTPS 網站(例如 https://bank.com),你輸入帳號密碼準備登入。此時,如果有駭客架設一台攔截設備(如假 Wi-Fi 熱點或公司網路的某種透明 Proxy),它可以:
- 攔截你的 HTTPS 連線請求
- 自己幫你連到真正的
bank.com
- 然後再把它「自製」的一張憑證發給你,試圖冒充成
bank.com
如果你的程式沒仔細驗證這張憑證(或用了 DangerousAcceptAnyServerCertificateValidator
這種忽略方式),你就會不知不覺地把帳號密碼送給駭客,而不是網站。
憑證驗證是阻擋 MITM 的最後防線,只要你的程式認真驗證憑證:
- 它會發現駭客發給你的憑證不是由可信的 CA 簽發
- 因此拒絕這次連線
- 使用者資料就不會外洩
這也是為什麼 TLS 的驗證責任一定要落在「我們」身上。如果我們不驗證,就等於打開了防線,讓攻擊者有機可乘。