STUN 應用於處理 NAT 穿越技術 (如 ICE ) 下的一種工具,本身並非 NAT 穿越的解決方案,主要功能為確認兩個在 NAT 背後節點的 IP / Port
,本篇為 RFC 5389 的閱讀心得,分享 STUN 背後的設計原理與結構
STUN 之上還有擴增一個 TURN 協定,提供 relay 連線的功能,一般的 TURN Server 會同時提供 STUN 的服務,如果想知道 TRUN Server 架設,可以參考另一篇文章 AWS Coturn server架設教學
如果你有以下疑惑,那這篇文章應該可以幫助到你
- STUN Server 究竟是如何運作
- STUN Server 是否有驗證機制
- 想知道 Coturn config 中的參數含義,如 Fingerprint / Realm / Nonce
與舊版 STUN 的差異
先前有一版 STUN 的定義 RFC-3489,當時的 STUN 被定義成完整的 NAT 穿越解決方案,但是遭遇了以下問題才改用這一版 5389 取代了舊版
- 無法正確區分 NAT 類型,導致可能連線有問題
NAT 共有四種類型,其中 symmetric NATs 是每次通訊沒有固定的 Public IP / Port,所以只能透過 TURN 來解決雙節點的連線問題
但是舊版 STUN 演算法設計無法區分 NAT 類型,所以有可能造成部分的連線異常 - 支援 TCP / DTLS:
舊版只支援 UDP - Security 考量
新版 STUN 不再是完整的 NAT traversal 解決方案,只專注於找出節點外層 NAT 對外的 Public IP/Port
,完整的解決方案如 ICE / SIP Outbound 等等
也因此 STUN SERVER 預設 port 是 3489 / TLS port 是 5398
架構介紹
可以看到 STUN Client 躲在兩層 NAT 之後,而 STUN Server 就是要通知 STUN Client 最外層 NAT 所對應的 Public IP / Port
STUN 採用 client/server 架構,支援兩種溝通方式(transaction)
- request / response
- indicate (送出去不等回覆)
每個 transaction 都有 96bit random id
傳送機制
每個 Transaction 會定義類型 (Action),目前 Spec 僅定義 Binding action
運作機制如下
- client 送出 request
- NAT 會修改 client package 封包的 IP,並自主管理 client private ip port 與 NAT 對應出去的 ip / port
- 一路到 server 手上只會拿到 NAT 的 public ip / port,稱之為
server reflexive transport address (srflx)
,如果有用過 WebRTC 應該有看過 srflx ,這就是代表用戶走 STUN - 接著 server 把這個 public ip / port 當作內容
XOR-MAPPED-ADDRESS
傳回去給 client - NAT 接著會一路改 ip port,但因為內文不改所以 client 會知道最外層 NAT 的 public ip + port
Packet Format
|
|
封包格式介紹
- 最前方兩個 bit 固定為 0 ,主要是用來區分是不是 STUN 的 packet
- Message Type 包含 Transaction 類型 / response 成功或失敗
- magic cookie 固定為 0x2112A442
- Transaction ID 用來區分 STUN transaction
- STUN message 不可以超過 MTO
- 如果傳輸透過 UDP:
Client 要自己處理 retransmit,預設 timeout 500ms ~ 3000 ms 間,之後每次 retry Timeout double - 如果傳輸透過 TCP:
不要增加而外的 framing / demultiplexing 已經保障資料可靠性,預設 Timeout 為 39.5s Server 應該等 Client 主動斷線,除非遇到 timeout
驗證訊息
Server 收到訊息後,會先做基本的驗證,如果有開啟 fingerprint extension 則驗證,如果發現有不支援的屬性則回傳錯誤;
Server 回傳錯誤時,必須指定 error code,並挾帶 error code attribute
,例如說有不支援的屬性則回傳 420 + UNKNOWN-ATTRIBUTES,如果是 401 驗證失敗則必須對應回傳驗證方式,這邊的 error code 參考 HTTP 所以會有種似曾相似的分類;
如果訊息成功則夾帶 XOR-MAPPED-ADDRESS
回傳
FINGERPRINT
再Multiplexing 下,會有多個不同 protocol 的 packet 傳送到相同位址的,例如說 RTP,加上 fingerprint
可以方便 STUN Server 區分 STUN message
DNS
透過 SRV 紀錄回傳 STUN Server 相關的服務,SRV 格式如下
|
|
如果是以 STUN Server 開放 TCP / UDP ,假設 STUN Server domain name 是 stun.example.com
的話
|
|
使用 SRV 好處是可以改變預設的 Port;
如果不用 SRV,也可以用 A / AAAA 紀錄返回 IP List,但 Port 就只能用預設的
身份驗證與訊息完整性檢查
共有兩種身份驗證的方式,
短期
Client / Server 會預先共享同一個 secret,後續 Client 產生一個有時間限制的 credential (password),Server 收到後會用相同的 secret 做驗證;
確保訊息完整性則透過 MESSAGE-INTEGRITY
欄位,此欄位產生的方式是把 STUN Message 做 HMAC_SHA1;
Server 收到STUN Message 後的驗證流程如下
- 沒有 MESSAGE-INTEGRITY 和 USERNAME 欄位則回傳 401
- 檢查 USERNAME 是否合法
- 用 passwrod 與 username 計算出 message intergrity 的值並比對
- 都通過則產生 response,response 同樣要包含 MESSAGE-INTEGRITY
Client 收到 response 也需要檢查 MESSAGE-INTEGRITY
因為 credential 有時間限制,所以不會遇到回放(replay)攻擊
長期
Server / Client 固定長期使用相同的 username / password
步驟
- Client 不帶任何 credential 發起
- Server reject,並分配 realm (指引 client 選擇 credential) 與 nonce (類似於 cookie,指定 duration / client identity 等) 多一層保護
- Client retry ,戴上 credential 與 nonce + message-integrity (對整個 request 做 HMAC)
- Server 檢查 auth 與 integrity
Server 收到訊息時會依序檢查
- MESSAGE-INTEGRITY: 沒有回傳 401
- 如果沒有 username / password / realm / nonce ,回傳 400
- Nonce 過期了,438
- Username 無效,401
- Message-integrity 錯誤,回傳 401
- ALTERNATE-SERVER Mechanism 如果 Server 希望 Redirect Client 去別的 Server,可以回傳 error code 300 並指定 ALTERNATE-SERVER Client 收到後,用相同的 transport protocol / credential 對新的 Server 重啟 transaction
之所以開頭要產生一次 auth failed 的 request是要了去跟 Server 拿 Nonce,主要是為了避免
回放攻擊
;
回放攻擊主要是 如果有中間人,他拿到 Client request 記錄下來,把同樣的 request 往 Server 送,因為 credential 是長期有效所以中間人也能夠通過驗證,即使是有 TLS 保護 / 中間人不知道 username, password 回放攻擊都有用;
所以才需要 Nonce 一個由 Server 核發一次性的 Token,包裝在 STUN Message 中,超出時間就會被認定無效,從而避免回放攻擊
STUN Attributes
再 STUN Header 之後,可以接零至多個 attribute,attribute 採用 TLV encode
|
|
如果 response 中有重複的 attribute,只有 第一個
會被考慮,其餘會被捨棄
Type 再 0x0000 and 0x7FFF 是必須要被處理的,如果 STUN agent 無法處理則會失敗
0x8000~0xFFFF 則是 optional
以下舉幾個常見的 attribute
MAPPED-ADDRESS
主要是為了兼容舊版 STUN,顯示 Client 最後一個 Public NAT 的 ip / port
XOR-MAPPED-ADDRESS
雷同於上者,但是 ip / port 都是跟 magic-cookie 前 16 bits 做 XOR 儲存 (ipv4 / ipv6 xor 方式不同),原因是發現有些 NAT 如果看到 payload 是自己的 public IP 會去修改,xor 之後就沒這個問題
原文解釋 deployment experience found that some NATs rewrite the 32-bit binary payloads containing the NAT’s public IP address, such as STUN’s MAPPED-ADDRESS attribute, in the well-meaning but misguided attempt at providing a generic ALG function.
USERNAME
用來驗證用的,必須是用 utf-8 encode 並小於 513 bets SASLprep
MESSAGE-INTEGRITY
對 STUN Message 取 HMAC-SHA1,固定長度為 20 bytes (因為 sha1 ) HMAC key 會因為 credential 不同而有所不同
- Long term:
key = MD5(username “:” realm “:” SASLprep(password))
- Short term:
key = SASLprep(password)
需注意 hash 包含整個 STUN Message,同時也包含了 Message length,所以在產生 hash 前,MESSAGE-INTEGRITY 也必須先安插進去 STUM Message 中並帶 dummy content,其他在之後的屬性則被排除在外 驗證時也必須遵守相同的流程
Fingerprint
對整個 STUN Message (排除自己) 取 CRC-32,接著與 0x5354554e 做 XOR ( 避免其他 packet 也用 CRC-32 Fingerprint 必須是最後一個屬性
|
|
Error Code
包含數值的 error code 從 300~699,保持與 SIP / HTTP 相似的羽翼 再加上 rease: utf-8的文字描述 ,主要是讓用戶可以理解
REALM
如果 STUN Server 同時支援多個 domain,透過 REALM 可以區分不同 domain 使用的設定
NONCE
用於每次連線避免 replay 問題的方式,類似於 web 的 cookie
UNKNOWN-ATTRIBUTES
再 error code 420 時出現
SOFTWARE
描述軟體的版本 / 製造商等資訊
ALTERNATE-SERVER
Server 要求轉換時
Security
以下條列幾種被攻擊的可能與防範措施
- Attacker 可能竄改 STUN 訊息:
但可以用 message integrity 驗證防止 - Attacker 可以居中回傳 error response
在某些如驗證失敗的訊息,這個就不好防堵,除非使用 TLS 才能杜絕問題 - HMAC可能遭受字典攻擊:
因為 STUN 利用 HMAC,可能遭受字典攻擊,請確保 password 足夠複雜,或是用 TLS 防止問題;不排除 SHA1 之後被攻破,未來可能增加新的欄位與新的 hash 機制 - DoS 部分:
STUN server 是stateless
,所以比較不會被 DoS 打垮;
Attacker 可能假冒 source IP,讓 STUN server 去攻擊受害者,攻擊不會被跨大,要從 ingress 去過濾 ip; - SOFTWARE 揭露版本資訊,可能變成潛藏的落點
STUN Server 應該要有對應的設定去關閉此選項 - 修改 source ip
Attack 居中的話,可以攔截client 的 source ip 並修改,server 收到錯的 ip 就會用 XOR-MAPPED-ADDRESS 回傳回,這幾乎不可能阻擋
,因為正常的 NAT 也會去修改 source ip;
只能在更上層的協議,例如 ICE 去驗證 address 的正確性