使用 SO_REUSEADDR和 SO_EXCLUSIVEADDRUSE
開發安全的高階網路基礎結構,是大部分網路應用程式開發人員的首要任務。 不過,儘管考慮完全安全的解決方案時非常重要,但套接字安全性通常被忽略。 具體而言,套接字安全性會處理系結至先前由另一個應用程式進程系結至相同埠的程式。 在過去,網路應用程式可能會「劫持」另一個應用程式的埠,這很容易導致「阻斷服務」攻擊或數據竊取。
一般而言,套接字安全性適用於伺服器端進程。 更具體來說,套接字安全性適用於任何接受連線和接收IP數據報流量的網路應用程式。 這些應用程式通常會系結至已知的埠,而且是惡意網路程式代碼的常見目標。
用戶端應用程式不太可能成為這類攻擊的目標,不是因為它們較易受攻擊,而是因為大多數客戶端系結至「暫時」本機埠,而不是靜態的「服務」埠。 除非有令人信服的架構原因,否則客戶端應用程式應一律系結至暫時埠(呼叫 bind 函數時,透過 名稱 參數指向的 SOCKADDR 結構指定埠 0)。 暫時性本地埠由大於 49151 的埠構成。 大部分專用服務的伺服器應用程式都會系結至小於或等於埠 49151 的已知保留埠。 因此,對大多數應用程式而言,用戶端與伺服器應用程式之間的系結要求通常不會發生衝突。
本節說明各種Microsoft Windows 平臺上的預設安全性層級,以及特定套接字選項如何 SO_REUSEADDR 和 SO_EXCLUSIVEADDRUSE 影響及影響網路應用程式安全性。 Windows Server 2003 和更新版本提供稱為增強型套接字安全性的其他功能。 這些套接字選項和增強型套接字安全性的可用性會因Microsoft作系統版本而異,如下表所示。
平臺 | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | 增強的套接字安全性 |
---|---|---|---|
Windows 95 | 可用 | 不可用 | 無法取得 |
Windows 98 | 可用 | 不可用 | 未提供 |
Windows Me | 可用 | 不可用 | 不可用 |
Windows NT 4.0 | 可用 | Service Pack 4 和更新版本中提供 | 不可用 |
Windows 2000 | 可用 | 可用 | 不可用 |
Windows XP | 可用 | 可用 | 未提供 |
Windows Server 2003 | 可用 | 可用 | 可用 |
Windows Vista | 可用 | 可用 | 可用 |
Windows Server 2008 | 可用 | 可用 | 可用 |
Windows 7 和更新版本 | 可供使用 | 可用 | 可用 |
使用SO_REUSEADDR
SO_REUSEADDR 套接字選項可讓套接字強制系結至另一個套接字所使用的埠。 第二個套接字會呼叫 setsockopt,並將 optname 參數設定為 SO_REUSEADDR,而 optval 參數會設定為布林值 TRUE,然後再呼叫 bind 到與原始套接字相同的埠。 第二個套接字成功系結之後,系結至該埠的所有套接字行為都不確定。 例如,如果相同埠上的所有套接字都提供 TCP 服務,則無法保證埠上的任何進入 TCP 連線要求會被正確的套接字處理,這種行為是不確定的。 惡意程式可以使用 SO_REUSEADDR 強制系結已用於標準網路通訊協定服務的套接字,以拒絕存取這些服務。 使用此選項不需要特殊許可權。
如果用戶端應用程式在伺服器應用程式能夠系結至相同的埠之前系結至埠,則可能會出現問題。 如果伺服器應用程式使用 [SO_REUSEADDR 套接字] 選項強制系結至相同的埠,則系結至該埠的所有套接字行為不確定。
非決定性行為的例外是多播套接字。 如果兩個套接字系結至相同的介面和埠,而且是相同多播群組的成員,則會將數據傳遞至這兩個套接字,而不是任意選擇的套接字。
使用SO_EXCLUSIVEADDRUSE
在引進 SO_EXCLUSIVEADDRUSE 套接字選項之前,網路應用程式開發者幾乎無計可施,無法阻止惡意程式綁定到他們的網路應用程式已綁定套接字的埠。 為了解決此安全性問題,Windows Sockets 引進了 SO_EXCLUSIVEADDRUSE 套接字選項,該選項可在 Windows NT 4.0 上搭配 Service Pack 4 (SP4) 和更新版本使用。
SO_EXCLUSIVEADDRUSE 套接字選項只能由 Windows XP 及更早版本中的管理員安全群組成員使用。 本文稍後將討論此需求在 Windows Server 2003 和更新版本上變更的原因。
設定 SO_EXCLUSIVEADDRUSE 選項的方式是呼叫 setsockopt 函式,將 optname 參數設定為 SO_EXCLUSIVEADDRUSE,並在系結套接字之前將 optval 參數設定為布爾值 TRUE。 設定選項之後,後續 系結 呼叫的行為會根據每個 系結 呼叫中指定的網路位址而有所不同。
下表描述在 Windows XP 和更早版本中,當第二個套接字嘗試使用特定套接字選項系結至由第一個套接字先前已系結的位址時所發生的行為。
注意
在下表中,「通配符」表示指定通訊協定的通配符位址(例如 IPv4 的 」0.0.0.0“ 和 IPv6 的 ”::“。 「特定」表示指派介面的特定IP位址。 資料表的儲存格顯示系結是否成功(“Success”),或是顯示錯誤(“INUSE”表示WSAEADDRINUSE 錯誤;“ACCESS”表示WSAEACCES 錯誤)。
第一個 綁定 呼叫 | 第二個 綁定 呼叫 | ||||||
預設 | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | |||||
通配符 | 特定 | 通配符 | 特定 | 通配符 | 特定 | ||
預設 | 通配符 | 使用中 | 使用中 | 成功 | 成功 | 使用中 | 使用中 |
特定 | 使用中 | 使用中 | 成功 | 成功 | 使用中 | 使用中 | |
SO_REUSEADDR | 通配符 | 使用中 | 使用中 | 成功 | 成功 | 使用中 | 使用中 |
特定 | 使用中 | 使用中 | 成功 | 成功 | 使用中 | 使用中 | |
SO_EXCLUSIVEADDRUSE | 通配符 | 使用中 | 使用中 | 存取 | 存取 | 使用中 | 使用中 |
特定 | 使用中 | 使用中 | 存取 | 存取 | 使用中 | 使用中 |
當兩個Socket綁定到相同的埠號碼,但位於不同的明確介面上時,不會發生衝突。 例如,如果計算機有兩個IP介面,10.0.0.0.1和10.99.99.99, 如果第一次呼叫 系結 是在10.0.0.1,且埠設定為5150且 SO_EXCLUSIVEADDRUSE 指定,則第二次呼叫 在10.99.99.99.99.999上系結,埠也設定為5150,且未指定任何選項會成功。 不過,如果第一個套接字系結至通配符位址和埠 5150,則 SO_EXCLUSIVEADDRUSE 設定之埠 5150 的任何後續系結呼叫都會失敗,WSAEADDRINUSE 或 WSAEACCES 由 系結 作業傳回。
如果第一次呼叫 系結 設定 SO_REUSEADDR 或完全沒有套接字選項,第二個 系結 呼叫將會「劫持」埠,而且應用程式將無法判斷兩個套接字中哪一個收到傳送至「共用」埠的特定封包。
呼叫 綁定 函式的一般應用程式不會分配綁定的套接字以供獨佔使用,除非在呼叫 綁定 函式之前,在套接字上呼叫 SO_EXCLUSIVEADDRUSE 套接字選項。 如果用戶端應用程式在伺服器應用程式系結至相同埠之前系結至暫時性埠或特定埠,可能會導致問題。 伺服器應用程式可以在呼叫 系結 函式之前,先使用套接字上的 SO_REUSEADDR 套接字選項強行系結至相同的埠,但系結至該埠的所有套接字行為則不確定。 如果伺服器應用程式嘗試使用 SO_EXCLUSIVEADDRUSE 套接字選項來獨占端口使用,請求將會失敗。
相反地,設定有 SO_EXCLUSIVEADDRUSE 的套接字可能無法在套接字關閉之後立即重複使用。 例如,如果一個設置了 SO_EXCLUSIVEADDRUSE 的監聽套接字接受了一個連線,然後被關閉,則在原始連線變成不活動狀態之前,另一個套接字(也設置了 SO_EXCLUSIVEADDRUSE)無法綁定到與第一個套接字相同的埠。
此問題可能會變得複雜,因為基礎傳輸通訊協定可能無法終止連線,即使套接字已關閉也一樣。 即使在應用程式關閉套接字之後,系統也必須傳輸任何緩衝的數據、將正常中斷連線訊息傳送至對等,以及等候對等的對應正常中斷連線訊息。 基礎傳輸通訊協定可能永遠不會釋放連線;例如,參與原始連線的對等可能會公告零大小的視窗,或某種形式的「攻擊」組態。 在這種情況下,儘管要求關閉用戶端連線,但用戶端連線仍處於作用中狀態,因為未認可的數據會保留在緩衝區中。
為了避免這種情況,網路應用程式應該呼叫已設定SD_SEND旗標的 關機,然後在 recv 迴圈中等候,直到透過連線傳回零個字節為止。 這可確保對等會接收所有數據,並同樣地向對等確認已接收所有傳輸的數據,以及避免上述埠重複使用問題。
SO_LINGER套接字選項可以在套接字上設定,以防止埠轉換為「作用中」等候狀態;不過,不建議這樣做,因為它可能會導致不想要的效果,例如重設連線。 例如,如果數據是由對等端接收但尚未確認,而本機計算機關閉設定有 SO_LINGER 的套接字,那麼兩部計算機之間的連線將被重設,並且未被確認的數據將被對等端丟棄。 選擇合適的逾時設定時間是困難的,因為較小的逾時值可能導致連線突然中斷,而較大的逾時值又會讓系統容易受到拒絕服務攻擊(透過建立大量連線並可能造成應用程式執行緒停滯或封鎖)。 關閉具有非零延遲時間的套接字,也可能會導致 closesocket 呼叫被阻塞。
增強套接字安全性
Windows Server 2003 版本已新增增強的套接字安全性。 在先前的 Microsoft 伺服器作業系統版本中,預設的套接字安全性輕易允許程序從不知情的應用程式劫持埠。 在 Windows Server 2003 中,套接字預設不會處於可共享狀態。 因此,如果應用程式想要允許其他進程重複使用已系結套接字的埠,則必須特別啟用它。 如果是這種情況,在埠上呼叫 系結 的第一個套接字必須在套接字上設定 SO_REUSEADDR。 唯一的例外狀況是,第二個 系結 呼叫是由對 系結進行原始呼叫的相同用戶帳戶執行。 此例外狀況只為了提供回溯相容性而存在。
下表描述在 Windows Server 2003 和更新版本的作業系統中,當第二個套接字嘗試使用特定套接字選項系結至已由第一個套接字系結的位址時所發生的行為。
注意
在下表中,「通配符」表示指定通訊協定的通配符位址(例如 IPv4 的 」0.0.0.0“ 和 IPv6 的 ”::“。 「特定」表示指派介面的特定IP位址。 表格中的儲存格指出系結是否成功(「Success」)或返回的錯誤(「INUSE」代表 WSAEADDRINUSE 錯誤;「ACCESS」代表 WSAEACCES 錯誤)。
另請注意,在此特定數據表中,綁定的 呼叫均在同一個用戶帳戶下進行。
第一個 綁定 呼叫 | 第二個 系結 呼叫 | ||||||
預設 | SO_REUSEADDR | SO_EXCLUSIVEADDRUSE | |||||
通配符 | 特定 | 通配符 | 特定 | 通配符 | 特定 | ||
預設 | 通配符 | 使用中 | 成功 | 存取 | 成功 | 使用中 | 성공 |
特定 | 成功 | 使用中 | 成功 | 存取 | 使用中 | 使用中 | |
SO_REUSEADDR(允許重複使用地址) | 通配符 | 使用中 | 成功 | 成功 | 成功 | 使用中 | 成功 |
特定 | 成功 | 使用中 | 成功 | 成功 | 使用中 | 使用中 | |
SO_EXCLUSIVEADDRUSE | 通配符 | 使用中 | 存取 | 存取 | 存取 | 使用中 | 存取 |
特定 | 成功 | 使用中 | 成功 | 存取 | 使用中 | 使用中 |
上述表格中的幾個條目值得說明。
例如,若第一個呼叫端在特定位址上設定 SO_EXCLUSIVEADDRUSE,而第二個呼叫端在同一個埠上以通配符位址嘗試呼叫 系結,則第二次的 系結 呼叫會成功。 在此特定案例中,第二個呼叫端會系結至所有介面,但第一個呼叫端所系結的特定位址除外。 請注意,此案例的相反情況不成立:如果第一個呼叫端設定 SO_EXCLUSIVEADDRUSE,且呼叫 綁定 使用通配符旗標,則第二個呼叫端無法用相同的埠呼叫 綁定。
套接字系結行為會在不同的用戶帳戶下呼叫套接字系結時變更。 下表指定 Windows Server 2003 和更新版本作業系統中,當第二個套接字使用特定的套接字選項和不同的使用者帳戶,嘗試系結至先前已被第一個套接字系結的位址時所發生的行為。
第一個 綁定 呼叫 | 第二個 系結 呼叫 | ||||||
預設 | SO_REUSEADDR | 專用地址使用 (SO_EXCLUSIVEADDRUSE) | |||||
通配符 | 特定 | 通配符 | 特定 | 通配符 | 特定 | ||
預設 | 通配符 | 使用中 | 存取 | 存取 | 存取 | 使用中 | 存取 |
特定 | 成功 | 使用中 | 成功 | 存取 | 使用中 | 使用中 | |
SO_REUSEADDR | 通配符 | 使用中 | 進入 | 成功 | 成功 | 使用中 | 存取 |
特定 | 成功 | 使用中 | 成功 | 成功 | 使用中 | 使用中 | |
SO_EXCLUSIVEADDRUSE | 通配符 | 使用中 | 存取 | 存取 | 存取 | 正在使用 | 存取 |
特定 | 成功 | 使用中 | 成功 | 存取 | 使用中 | 正在使用中 |
請注意,當 系結 呼叫在不同的用戶帳戶下進行時,默認行為會有所不同。 如果第一個呼叫端未在套接字上設定任何選項並系結至通配符位址,則第二個呼叫端無法設定 SO_REUSEADDR 選項,並成功系結至相同的埠。 未設定任何選項的預設行為也會傳回錯誤。
在 Windows Vista 和更新版本上,可以建立可透過 IPv6 和 IPv4 運作的雙堆棧套接字。 當雙重堆疊套接字綁定到通配符位址時,所給定的端口會在 IPv4 和 IPv6 網路堆疊上保留,並執行與 SO_REUSEADDR 和 SO_EXCLUSIVEADDRUSE (如果已設定)相關聯的檢查。 這兩個網路協定棧上的檢查都必須成功。 例如,如果雙堆疊 TCP 套接字設定 SO_EXCLUSIVEADDRUSE,然後嘗試綁定至端口 5000,則不允許任何其他 TCP 套接字事先綁定至端口 5000(不論是通配符或特定)。 在此情況下,如果 IPv4 TCP 套接字先前已綁定至端口 5000 的回送地址,則對雙堆疊套接字的 綁定 呼叫將以 WSAEACCES的方式失敗。
應用程式策略
開發在套接字層運作的網路應用程式時,請務必考慮所需的套接字安全性類型。 用戶端應用程式 — 連接或傳送數據至服務的應用程式 — 很少需要任何其他步驟,因為它們系結至隨機本機 (暫時) 埠。 如果客戶端確實需要特定的本機埠系結才能正常運作,則必須考慮套接字安全性。
除了多播套接字之外,SO_REUSEADDR 選項在一般應用程式中很少使用,因為多播套接字會將數據傳遞到相同埠上系結的所有套接字。 否則,任何設定此套接字選項的應用程式都應該重新設計以移除相依性,因為它非常容易受到「套接字劫持」的影響。 只要 SO_REUSEADDR 套接字選項可用來可能劫持伺服器應用程式中的埠,應用程式就必須被視為不安全。
所有伺服器應用程式都必須設定 SO_EXCLUSIVEADDRUSE,以達到較高的套接字安全性。 它不僅會防止惡意軟體劫持埠,也會指出另一個應用程式是否系結至要求的埠。 例如,若另一個進程目前已經在特定介面上綁定了相同的端口,則帶有 SO_EXCLUSIVEADDRUSE 套接字選項的進程呼叫 綁定 至通配符位址將會失敗。
最後,即使 Windows Server 2003 中已改善套接字安全性,應用程式應該一律設定 SO_EXCLUSIVEADDRUSE 套接字選項,以確保它會系結至進程所要求的所有特定介面。 Windows Server 2003 中的套接字安全性增加了舊版應用程式的安全性層級,但應用程式開發人員仍必須以安全性的所有層面設計其產品。