WWDC22 - DNS安全:DNSSEC
我正在參加「掘金·啟航計劃」
引言
作為客戶端開發者,DNS的相關的領域知識,一般都不太關注,然而隨著你App使用者增長的一定量級,而你又是那個cover整個App的人,你就一定會開始注意它,它自身的安全性問題常常會引發一些問題。
另外,作為客戶端開發者,從你發起請求那一刻到頁面渲染出來所經歷的過程,你最好都清楚掌握。
什麼是DNS
DNS 即域名系統,全稱是 Domain Name System。當我們訪問一個 URL 地址時,瀏覽器要向這個 URL 的主機名對應的伺服器傳送請求,就得知道伺服器的 IP,對於瀏覽器來說,DNS 的作用就是將主機名轉換成 IP 地址。也就是,DNS 是一個應用層協議,使用的是53埠,我們傳送一個請求,其中包含我們要查詢的主機名,它就會給我們返回這個主機名對應的 IP。
其次,DNS 是一個分散式資料庫,整個 DNS 系統由分散在世界各地的很多臺 DNS 伺服器組成,每臺 DNS 伺服器上都儲存了一些資料,這些資料可以讓我們最終查到主機名對應的 IP。
所以 DNS 的查詢過程,說白了,就是去向這些 DNS 伺服器詢問,你知道這個主機名的 IP 是多少嗎,不知道?那你知道去哪臺 DNS 伺服器上可以查到嗎?直到查到我想要的 IP 為止。
DNS的層次結構
DNS 伺服器有 3 種類型:根 DNS 伺服器、頂級域(Top-Level Domain, TLD)DNS 伺服器和權威 DNS 伺服器。它們的層次結構我們可以看一張session中的圖
-
根DNS伺服器
- 以
www.baidu.com
為例,它的完整寫法應該:www.baidu.com``.
,最後的.
就是根域名。根DNS伺服器的作用就是管理它的下一級-----頂級域DNS伺服器。 - 通過詢問跟DNS伺服器,我們可以知道一個主機名對應的頂級域DNS伺服器的IP是多少,從而繼續向頂級域DNS伺服器發起查詢請求。
- 以
-
頂級域DNS伺服器
- 除了上面提到的
www.baidu.com
中的com
是頂級域名,常見的頂級域名還有cn
、org
、edu
等。頂級域 DNS 伺服器,也就是 TLD,提供了它的下一級,也就是權威 DNS 伺服器的 IP 地址。
- 除了上面提到的
-
權威DNS伺服器
- 權威 DNS 伺服器返回主機 - IP 的最終對映
還有另一類重要的DNS伺服器,稱為本地DNS伺服器(local DNS server)。嚴格說來,一個本地DNS伺服器並不屬於該DNS的層次結構,但它對DNS層次結構是至關重要的。本地DNS伺服器通常與主機相隔不超過幾臺路由器。當主機發岀 DNS請求時,該請求被髮往本地DNS伺服器,它起著代理的作用,並將該請求轉發到DNS伺服器層次結構中,下面的流程圖,完整展示了DNS伺服器解析IP的全過程,如下圖所示:
DNS安全問題
網際網路協議,如 TCP 、TLS 和 QUIC ,依賴於 IP 地址,因此一切都是從 DNS 開始。今天 TLS 被廣泛用於保護網際網路通訊安全,但作為基礎層的 DNS 存在一些安全問題。DNS 有史以來並不安全,它是在 1983 年設計的,當時幾乎沒有什麼安全考慮,從那以後的幾年裡,已經產生了很多 DNS 攻擊。
DNS 快取中毒
攻擊者利用 DNS 解析的缺陷,使他們快取不正確的 IP 地址,導致客戶端連線到惡意主機。這揭示了 DNS 的一個漏洞:它沒有身份驗證。如今傳統的 DNS 客戶端無法驗證應答者,因此很容易被欺騙。
主要的操作步驟有:
-
攻擊機器向 DNS 轉發器傳送大量查詢報文
-
DNS 轉發器向 DNS 域名伺服器解析查詢域名
-
攻擊機器偽裝成 DNS 域名解析伺服器,向轉發器回覆偽造的響應報文,並實現投毒
DNS嗅探
網路流量是透明的,預設情況下 DNS 的報文是沒有加密的,因此在傳輸的過程中,攻擊者可以監聽客戶端和 DNS 解析伺服器之間的 DNS 流量,收集客戶端的歷史記錄。對於使用者隱私來說,這是一個嚴重的問題。而讓這種攻擊成為可能的原因是 DNS 流量最初是未加密的。
為了成為安全起點,在此之上構建其他的協議,DNS 需要經過身份驗證和加密。
-
當我們使用 DNSSEC 對 DNS 響應進行簽名時,它提供了身份驗證
-
當我使用 TLS 和 HTTPS 加密 DNS 解析結果時,它可以確保隱私
TLS我們在後面一篇分享中再著重講,接下來我們先看DNSSEC。
DNSSEC
DNSSEC是DNS安全擴充套件,主要思想是通過在DNS記錄中新增加密簽名,從而為DNS解析流程提供來源可認證和資料完整性的保障。
iOS16 和 macOS Ventura 現在支援客戶端 DNSSEC 驗證。DNSSEC 通過在響應中附加簽名來保護資料完整性。如果響應被攻擊者篡改了,那麼篡改後的資料簽名將與原始資料不匹配。這種情況下,客戶端可以檢測到響應已經被篡改然後將其丟棄。
DNSSEC 還通過使用特殊型別的 DNS 記錄(例如 NSEC 記錄)來斷言區域中記錄是否存在。NSEC 記錄按字母順序安全地告訴您下一個記錄名稱是什麼。只有它列出的名稱才是存在的,任何未列出的名稱都是不存在的。
例如,現在這裡有三個 NSEC 記錄。記錄集合中顯示區域 org 只有三個記錄名稱,A.org、C.org 和 E.org。如果有攻擊者說 A.org 不存在,客戶端可以檢測到這種攻擊。確定 A.org 的確存在,因為它列在第一個 NSEC 記錄中。同樣的,如果攻擊者說 D.org 存在,客戶端也可以檢測到,因為根據第二個 NSEC 記錄,D.org 位於 C.org 和 E.org 之間,但是這兩個名稱之間並不存在 D.org。
DNSSEC 通過建立信任鏈來驗證記錄,舉個例子:裝置想要解析 www.example.org 並啟用 DNSSEC 驗證,過程如下:
-
傳送詢問 IP 地址、簽名和金鑰的查詢,通過響應可以建立從 IP 地址到金鑰 1 的信任關係
-
客戶端向父區域 org 傳送查詢,詢問可用於驗證金鑰 1 的記錄,建立從金鑰 1 到金鑰 2 的信任關係
-
裝置遞迴地重複這個過程,直到它到達根域
現在如果根金鑰(圖中的金鑰 3 )可以信任,則可以驗證從 IP 地址到金鑰 3 的信任關係。根金鑰的雜湊值始終安全地儲存在裝置中。在 DNSSEC 中,它被稱為根信任錨。如果金鑰 3 的雜湊值與預先安裝的錨匹配,則可以安全地建立信任鏈。通過信任鏈,www.example.org 的 IP 地址現在已經通過了身份驗證。
支援 DNSSEC
如果你想在你的應用程式中要求 DNSSEC 驗證,那麼你需要如下操作:
-
域名支援 IPV6
-
對域名進行簽名
-
採用相應架構的 APIs
在只有 IPV6 的環境中,IPV4 地址被轉換為合成 IPV6 地址。如果域名被簽名,合成地址無法通過 DNSSEC 驗證; 啟用 DNSSEC 後,它們無法訪問。因此,請確保域支援 IPv6。
確保你的 DNS 服務提供商使用 DNSSEC 對你的域名進行簽名。如果你在您的應用程式中啟用了 DNSSEC 但是沒有對你的域名進行簽名,為了嘗試對沒有簽名的域名進行身份驗證,DNS解析時間會增加。
獲得相應的基礎架構支援後,以下是為你的應用採用 DNSSEC 所需要的程式碼。
Swift
// Require DNSSEC validation in your URL request at session level.
let configuration = URLSessionConfiguration.default
configuration.requiresDNSSECValidation = true
let session = URLSession(configuration: configuration)
如果你是 URLSession 客戶端,你可以要求對你的 URL 請求進行 DNSSEC 驗證。舉個例子:
Swift
var request = URLRequest(url: URL(string: "http://www.xxx.com")!)
request.requiresDNSSECValidation = true
let (data, response) = try await URLSession.shared.data(for: request)
先建立一個預設會話配置,設定需要 DNSSEC 驗證。接下來將使用修改後的配置來建立會話。它會為從此會話建立的所有 URL 請求啟用 DNSSEC。如果你不想在整個會話啟用 DNSSEC,你也可以在請求級別執行此操作。
如果你是 Network.framework 客戶端,你還可以要求對連線進行 DNSSEC 驗證。建立引數物件時,需要進行 DNSSEC 驗證,然後使用引數物件建立 NWConnection
Swift
// Require DNSSEC validation in your network request.
let parameters = NWParameters.tls
parameters.requiresDNSSECValidation = true
let connection = NWConnection(host: "www.example.org", port: .https, using: parameters)
當你開始連線時,只有在 DNSSEC 驗證完成並且與經過驗證的 IP 地址建立連線時,它才會進入就緒狀態。啟用 DNSSEC 後,將僅使用經過驗證的地址來建立連線。
在 DNSSEC 中,驗證失敗不會返回任何錯誤。收到驗證失敗的響應等於沒有收到任何響應。
如果存在篡改響應的 DNS 提供者,地址將無法通過身份驗證檢查,因此將直接丟棄。當裝置加入 DNS 提供商未篡改響應的新網路時,驗證將再次進行,解析將自動恢復正常。
以下是一些可能導致 DNSSEC 失敗的情況。
-
當原始 DNS 響應被修改了,不匹配的簽名將無法通過 DNSSEC 檢查,從而導致驗證失敗
-
當裝置無法訪問任何預安裝的信任錨並且無法與其建立信任鏈時
-
網路不支援 DNSSEC 所需的必要協議,例如 DNS over TCP 和 EDNS0 選項
-
當簽名的域名不支援 IPv6 時,由網際網路服務提供商提供的合成 IPv6 地址將無法通過驗證
這就是使用 DNSSEC 對 DNS 響應進行身份驗證的方法,但如果它們仍未加密,網路上的任何人都可以看到它們。
DDR 對 DNS 加密
iOS 14引入了加密 DNS 以幫助保護隱私。可以使用應用程式中的 NEDNSSettingsManager
或配置檔案中的 DNSSettings
手動配置加密的 DNS 系統範圍。還可以使用 NWParameters
上的 PrivacyContext
為您的應用程式選擇加密 DNS。在 iOS 16 中新增了可以自動使用加密的 DNS的新功能。
如果您的網路支援發現指定解析器(也稱為 DDR ),則 DNS 查詢將自動使用 TLS 或 HTTPS。要使用加密的 DNS,您的裝置需要知道解析器支援 TLS 或 HTTPS,並且可能還需要學習埠或 URL 路徑。諸如 DHCP 或路由器播發等常見機制僅提供普通 IP 地址。DDR 是 Apple 與其他行業合作伙伴在 IETF 中開發的一種新協議。
它為 DNS 客戶端提供了一種通過使用特殊 DNS 查詢來了解這些必要資訊的方法。當你的裝置加入新網路時,它將發出“_dns.resolver.arpa”的服務繫結查詢。如果 DNS 伺服器支援 DDR,它會回覆一個或多個配置。然後,裝置使用此資訊建立與指定解析器的加密連線。它驗證未加密解析器的 IP 地址是否包含在指定解析器的 TLS 證書中。這樣做是為了確保未加密的解析器和加密的解析器屬於同一實體。
如果一切正常,裝置現在預設使用加密的 DNS。DDR 一次適用於單個網路。只有在當前網路支援時,你的裝置才會自動使用加密的 DNS。同樣重要的是要注意,如果 DNS 伺服器的 IP 地址是私有 IP 地址,則 DDR 不起作用。這是因為 TLS 證書中不允許此類 IP 地址,因為它們的所有權無法驗證。
在 iOS 16還支援在使用加密 DNS 進行配置設定時使用 NEDNSSettingsManager
或 DNSSettings
配置檔案,指定客戶端身份驗證的功能。
現在可以使用 NEDNSSettings
的 identityReference
屬性配置客戶端證書。就像 VPN 的客戶端證書一樣。這些可以應用於 DNS over TLS 和 DNS over HTTPS。這是保護 DNS 的途徑。
寫到最後
我在招聘面試的時候,問一些似乎非客戶端的知識問題時,偶爾會遇到以下的反問:
-
作為一個客戶端開發,或者說前端開發,我們為什麼要去了解這些內容呢?
-
這些內容似乎在我們的工作中不曾遇到,我們瞭解不瞭解,清楚不清楚又有什麼關係呢?
當然還有一些沒反問的,可能僅僅是缺了反問的勇氣,內心的想法還是一樣的。對於這類問題,我不好判定他們想法對不對。
在我看來,很多知識點看起來似乎在我們日常工作中用不到,但是能幫助我們從一個更巨集觀更全域性的角度去理解我們面對的需求或者說工程專案。
跳出單個業務,跳出單個團隊看問題,其實是非常重要的,而這些能力素養,其實就是在更加全面的知識積累中萌生出來的。