深入淺出DDoS攻擊防禦

 

文/魏興國

敵情篇 ——DDoS攻擊原理

DDoS攻擊基礎

DDoS(Distributed Denial of Service,分佈式拒絕服務)攻擊的主要目的是讓指定目標無法提供正常服務,甚至從互聯網上消失,是目前最強大、最難防禦的攻擊之一。

按照發起的方式,DDoS可以簡單分爲三類。

第一類以力取勝,海量數據包從互聯網的各個角落蜂擁而來,堵塞IDC入口,讓各種強大的硬件防禦系統、快速高效的應急流程無用武之地。這種類型的攻擊典型代表是ICMP Flood和UDP Flood,現在已不常見。

第二類以巧取勝,靈動而難以察覺,每隔幾分鐘發一個包甚至只需要一個包,就可以讓豪華配置的服務器不再響應。這類攻擊主要是利用協議或者軟件的漏洞發起,例如Slowloris攻擊、Hash衝突攻擊等,需要特定環境機緣巧合下才能出現。

第三類是上述兩種的混合,輕靈渾厚兼而有之,既利用了協議、系統的缺陷,又具備了海量的流量,例如SYN Flood攻擊、DNS Query Flood攻擊,是當前的主流攻擊方式。

本文將一一描述這些最常見、最具代表性攻擊方式,並介紹它們的防禦方案。

SYN Flood

SYN Flood是互聯網上最經典的DDoS攻擊方式之一,最早出現於1999年左右,雅虎是當時最著名的受害者。SYN Flood攻擊利用了TCP三次握手的缺陷,能夠以較小代價使目標服務器無法響應,且難以追查。

標準的TCP三次握手過程如下:

  • 客戶端發送一個包含SYN標誌的TCP報文,SYN即同步(Synchronize),同步報文會指明客戶端使用的端口以及TCP連接的初始序號;
  • 服務器在收到客戶端的SYN報文後,將返回一個SYN+ACK(即確認Acknowledgement)的報文,表示客戶端的請求被接受,同時TCP初始序號自動加1;
  • 客戶端也返回一個確認報文ACK給服務器端,同樣TCP序列號被加1。

經過這三步,TCP連接就建立完成。TCP協議爲了實現可靠傳輸,在三次握手的過程中設置了一些異常處理機制。第三步中如果服務器沒有收到客戶端的最終ACK確認報文,會一直處於SYN_RECV狀態,將客戶端IP加入等待列表,並重發第二步的SYN+ACK報文。重發一般進行3-5次,大約間隔30秒左右輪詢一次等待列表重試所有客戶端。另一方面,服務器在自己發出了SYN+ACK報文後,會預分配資源爲即將建立的TCP連接儲存信息做準備,這個資源在等待重試期間一直保留。更爲重要的是,服務器資源有限,可以維護的SYN_RECV狀態超過極限後就不再接受新的SYN報文,也就是拒絕新的TCP連接建立。

SYN Flood正是利用了上文中TCP協議的設定,達到攻擊的目的。攻擊者僞裝大量的IP地址給服務器發送SYN報文,由於僞造的IP地址幾乎不可能存在,也就幾乎沒有設備會給服務器返回任何應答了。因此,服務器將會維持一個龐大的等待列表,不停地重試發送SYN+ACK報文,同時佔用着大量的資源無法釋放。更爲關鍵的是,被攻擊服務器的SYN_RECV隊列被惡意的數據包占滿,不再接受新的SYN請求,合法用戶無法完成三次握手建立起TCP連接。也就是說,這個服務器被SYN Flood拒絕服務了。

對SYN Flood有興趣的可以看看http://www.icylife.net/yunshu/show.php?id=367,這是我2006年寫的代碼,後來做過幾次修改,修改了Bug,並降低了攻擊性,純做測試使用。

DNS Query Flood

作爲互聯網最基礎、最核心的服務,DNS自然也是DDoS攻擊的重要目標之一。打垮DNS服務能夠間接打垮一家公司的全部業務,或者打垮一個地區的網絡服務。前些時候風頭正盛的黑客組織anonymous也曾經宣佈要攻擊全球互聯網的13臺根DNS服務器,不過最終沒有得手。

UDP攻擊是最容易發起海量流量的攻擊手段,而且源IP隨機僞造難以追查。但過濾比較容易,因爲大多數IP並不提供UDP服務,直接丟棄UDP流量即可。所以現在純粹的UDP流量攻擊比較少見了,取而代之的是UDP協議承載的DNS Query Flood攻擊。簡單地說,越上層協議上發動的DDoS攻擊越難以防禦,因爲協議越上層,與業務關聯越大,防禦系統面臨的情況越複雜。

DNS Query Flood就是攻擊者操縱大量傀儡機器,對目標發起海量的域名查詢請求。爲了防止基於ACL的過濾,必須提高數據包的隨機性。常用的做法是UDP層隨機僞造源IP地址、隨機僞造源端口等參數。在DNS協議層,隨機僞造查詢ID以及待解析域名。隨機僞造待解析域名除了防止過濾外,還可以降低命中DNS緩存的可能性,儘可能多地消耗DNS服務器的CPU資源。

關於DNS Query Flood的代碼,我在2011年7月爲了測試服務器性能曾經寫過一份代碼,鏈接是http://www.icylife.net/yunshu/show.php?id=832。同樣的,這份代碼人爲降低了攻擊性,只做測試用途。

HTTP Flood

上文描述的SYN Flood、DNS Query Flood在現階段已經能做到有效防禦了,真正令各大廠商以及互聯網企業頭疼的是HTTP Flood攻擊。HTTP Flood是針對Web服務在第七層協議發起的攻擊。它的巨大危害性主要表現在三個方面:發起方便、過濾困難、影響深遠。

SYN Flood和DNS Query Flood都需要攻擊者以root權限控制大批量的傀儡機。收集大量root權限的傀儡機很花費時間和精力,而且在攻擊過程中傀儡機會由於流量異常被管理員發現,攻擊者的資源快速損耗而補充緩慢,導致攻擊強度明顯降低而且不可長期持續。HTTP Flood攻擊則不同,攻擊者並不需要控制大批的傀儡機,取而代之的是通過端口掃描程序在互聯網上尋找匿名的HTTP代理或者SOCKS代理,攻擊者通過匿名代理對攻擊目標發起HTTP請求。匿名代理是一種比較豐富的資源,花幾天時間獲取代理並不是難事,因此攻擊容易發起而且可以長期高強度的持續。

另一方面,HTTP Flood攻擊在HTTP層發起,極力模仿正常用戶的網頁請求行爲,與網站業務緊密相關,安全廠商很難提供一套通用的且不影響用戶體驗的方案。在一個地方工作得很好的規則,換一個場景可能帶來大量的誤殺。

最後,HTTP Flood攻擊會引起嚴重的連鎖反應,不僅僅是直接導致被攻擊的Web前端響應緩慢,還間接攻擊到後端的Java等業務層邏輯以及更後端的數據庫服務,增大它們的壓力,甚至對日誌存儲服務器都帶來影響。

有意思的是,HTTP Flood還有個頗有歷史淵源的暱稱叫做CC攻擊。CC是Challenge Collapsar的縮寫,而Collapsar是國內一家著名安全公司的DDoS防禦設備。從目前的情況來看,不僅僅是Collapsar,所有的硬件防禦設備都還在被挑戰着,風險並未解除。

慢速連接攻擊

提起攻擊,第一反應就是海量的流量、海量的報文。但有一種攻擊卻反其道而行之,以慢著稱,以至於有些攻擊目標被打死了都不知道是怎麼死的,這就是慢速連接攻擊,最具代表性的是rsnake發明的Slowloris。

HTTP協議規定,HTTP Request以\r\n\r\n結尾表示客戶端發送結束,服務端開始處理。那麼,如果永遠不發送\r\n\r\n會如何?Slowloris就是利用這一點來做DDoS攻擊的。攻擊者在HTTP請求頭中將Connection設置爲Keep-Alive,要求Web Server保持TCP連接不要斷開,隨後緩慢地每隔幾分鐘發送一個key-value格式的數據到服務端,如a:b\r\n,導致服務端認爲HTTP頭部沒有接收完成而一直等待。如果攻擊者使用多線程或者傀儡機來做同樣的操作,服務器的Web容器很快就被攻擊者佔滿了TCP連接而不再接受新的請求。

很快的,Slowloris開始出現各種變種。比如POST方法向Web Server提交數據、填充一大大Content-Length但緩慢的一個字節一個字節的POST真正數據內容等等。關於Slowloris攻擊,rsnake也給出了一個測試代碼,參見http://ha.ckers.org/slowloris/slowloris.pl

DDoS攻擊進階

混合攻擊

以上介紹了幾種基礎的攻擊手段,其中任意一種都可以用來攻擊網絡,甚至擊垮阿里、百度、騰訊這種巨型網站。但這些並不是全部,不同層次的攻擊者能夠發起完全不同的DDoS攻擊,運用之妙,存乎一心。

高級攻擊者從來不會使用單一的手段進行攻擊,而是根據目標環境靈活組合。普通的SYN Flood容易被流量清洗設備通過反向探測、SYN Cookie等技術手段過濾掉,但如果在SYN Flood中混入SYN+ACK數據包,使每一個僞造的SYN數據包都有一個與之對應的僞造的客戶端確認報文,這裏的對應是指源IP地址、源端口、目的IP、目的端口、TCP窗口大小、TTL等都符合同一個主機同一個TCP Flow的特徵,流量清洗設備的反向探測和SYN Cookie性能壓力將會顯著增大。其實SYN數據報文配合其他各種標誌位,都有特殊的攻擊效果,這裏不一一介紹。對DNS Query Flood而言,也有獨特的技巧。

首先,DNS可以分爲普通DNS和授權域DNS,攻擊普通DNS,IP地址需要隨機僞造,並且指明服務器要求做遞歸解析;但攻擊授權域DNS,僞造的源IP地址則不應該是純隨機的,而應該是事先收集的全球各地ISP的DNS地址,這樣才能達到最大攻擊效果,使流量清洗設備處於添加IP黑名單還是不添加IP黑名單的尷尬處境。添加會導致大量誤殺,不添加黑名單則每個報文都需要反向探測從而加大性能壓力。

另一方面,前面提到,爲了加大清洗設備的壓力不命中緩存而需要隨機化請求的域名,但需要注意的是,待解析域名必須在僞造中帶有一定的規律性,比如說只僞造域名的某一部分而固化一部分,用來突破清洗設備設置的白名單。道理很簡單,騰訊的服務器可以只解析騰訊的域名,完全隨機的域名可能會直接被丟棄,需要固化。但如果完全固定,也很容易直接被丟棄,因此又需要僞造一部分。

其次,對DNS的攻擊不應該只着重於UDP端口,根據DNS協議,TCP端口也是標準服務。在攻擊時,可以UDP和TCP攻擊同時進行。

HTTP Flood的着重點,在於突破前端的cache,通過HTTP頭中的字段設置直接到達Web Server本身。另外,HTTP Flood對目標的選取也非常關鍵,一般的攻擊者會選擇搜索之類需要做大量數據查詢的頁面作爲攻擊目標,這是非常正確的,可以消耗服務器儘可能多的資源。但這種攻擊容易被清洗設備通過人機識別的方式識別出來,那麼如何解決這個問題?很簡單,儘量選擇正常用戶也通過APP訪問的頁面,一般來說就是各種Web API。正常用戶和惡意流量都是來源於APP,人機差別很小,基本融爲一體難以區分。

之類的慢速攻擊,是通過巧妙的手段佔住連接不釋放達到攻擊的目的,但這也是雙刃劍,每一個TCP連接既存在於服務端也存在於自身,自身也需要消耗資源維持TCP狀態,因此連接不能保持太多。如果可以解決這一點,攻擊性會得到極大增強,也就是說Slowloris可以通過stateless的方式發動攻擊,在客戶端通過嗅探捕獲TCP的序列號和確認維護TCP連接,系統內核無需關注TCP的各種狀態變遷,一臺筆記本即可產生多達65535個TCP連接。

前面描述的,都是技術層面的攻擊增強。在人的方面,還可以有一些別的手段。如果SYN Flood發出大量數據包正面強攻,再輔之以Slowloris慢速連接,多少人能夠發現其中的祕密?即使服務器宕機了也許還只發現了SYN攻擊想去加強TCP層清洗而忽視了應用層的行爲。種種攻擊都可以互相配合,達到最大的效果。攻擊時間的選擇,也是一大關鍵,比如說選擇維護人員吃午飯時、維護人員下班堵在路上或者在地鐵裏無線上網卡都沒有信號時、目標企業在舉行大規模活動流量飆升時等。

這裏描述的只是純粹的攻擊行爲,因此不提供代碼,也不做深入介紹。

來自P2P網絡的攻擊

前面的攻擊方式,多多少少都需要一些傀儡機,即使是HTTP Flood也需要搜索大量的匿名代理。如果有一種攻擊,只需要發出一些指令,就有機器自動上來執行,纔是完美的方案。這種攻擊已經出現了,那就是來自P2P網絡的攻擊。

大家都知道,互聯網上的P2P用戶和流量都是一個極爲龐大的數字。如果他們都去一個指定的地方下載數據,使成千上萬的真實IP地址連接過來,沒有哪個設備能夠支撐住。拿BT下載來說,僞造一些熱門視頻的種子,發佈到搜索引擎,就足以騙到許多用戶和流量了,但這只是基礎攻擊。

高級P2P攻擊,是直接欺騙資源管理服務器。如迅雷客戶端會把自己發現的資源上傳到資源管理服務器,然後推送給其他需要下載相同資源的用戶,這樣,一個鏈接就發佈出去。通過協議逆向,攻擊者僞造出大批量的熱門資源信息通過資源管理中心分發出去,瞬間就可以傳遍整個P2P網絡。更爲恐怖的是,這種攻擊是無法停止的,即使是攻擊者自身也無法停止,攻擊一直持續到P2P官方發現問題更新服務器且下載用戶重啓下載軟件時爲止。

 

應對篇 ——DDoS防禦方案

防禦基礎

攻擊流量到底多大

談到DDoS防禦,首先就是要知道到底遭受了多大的攻擊。這個問題看似簡單,實際上卻有很多不爲人知的細節在裏面。

以SYN Flood爲例,爲了提高發送效率在服務端產生更多的SYN等待隊列,攻擊程序在填充包頭時,IP首部和TCP首部都不填充可選的字段,因此IP首部長度恰好是20字節,TCP首部也是20字節,共40字節。

對於以太網來說,最小的包長度數據段必須達到46字節,而攻擊報文只有40字節,因此,網卡在發送時,會做一些處理,在TCP首部的末尾,填充6個0來滿足最小包的長度要求。這個時候,整個數據包的長度爲14字節的以太網頭,20字節的IP頭,20字節的TCP頭,再加上因爲最小包長度要求而填充的6個字節的0,一共是60字節。

但這還沒有結束。以太網在傳輸數據時,還有CRC檢驗的要求。網卡會在發送數據之前對數據包進行CRC檢驗,將4字節的CRC值附加到包頭的最後面。這個時候,數據包長度已不再是40字節,而是變成64字節了,這就是常說的SYN小包攻擊,數據包結構如下:

|14字節以太網頭部|20字節IP頭部|20字節TCP|6字節填充|4字節檢驗|
|目的MAC|源MAC|協議類型| IP頭 |TCP頭|以太網填充 | CRC檢驗 |

到64字節時,SYN數據包已經填充完成,準備開始傳輸了。攻擊數據包很小,遠遠不夠最大傳輸單元(MTU)的1500字節,因此不會被分片。那麼這些數據包就像生產流水線上的罐頭一樣,一個包連着一個包緊密地擠在一起傳輸嗎?事實上不是這樣的。

以太網在傳輸時,還有前導碼(preamble)和幀間距(inter-frame gap)。其中前導碼佔8字節(byte),即64比特位。前導碼前面的7字節都是10101010,1和0間隔而成。但第八個字節就變成了10101011,當主機監測到連續的兩個1時,就知道後面開始是數據了。在網絡傳輸時,數據的結構如下:

|8字節前導碼|6字節目的MAC地址|6字節源MAC地址|2字節上層協議類型|20字節IP頭|20字節TCP頭|6字節以太網填充|4字節CRC檢驗|12字節幀間距|

也就是說,一個本來只有40字節的SYN包,在網絡上傳輸時佔的帶寬,其實是84字節。

有了上面的基礎,現在可以開始計算攻擊流量和網絡設備的線速問題了。當只填充IP頭和TCP頭的最小SYN包跑在以太網絡上時,100Mbit的網絡,能支持的最大PPS(Packet Per Second)是100×106 / (8 * (64+8+12)) = 148809,1000Mbit的網絡,能支持的最大PPS是1488090。

SYN Flood防禦

前文描述過,SYN Flood攻擊大量消耗服務器的CPU、內存資源,並佔滿SYN等待隊列。相應的,我們修改內核參數即可有效緩解。主要參數如下:

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_synack_retries = 2

分別爲啓用SYN Cookie、設置SYN最大隊列長度以及設置SYN+ACK最大重試次數。

SYN Cookie的作用是緩解服務器資源壓力。啓用之前,服務器在接到SYN數據包後,立即分配存儲空間,並隨機化一個數字作爲SYN號發送SYN+ACK數據包。然後保存連接的狀態信息等待客戶端確認。啓用SYN Cookie之後,服務器不再分配存儲空間,而且通過基於時間種子的隨機數算法設置一個SYN號,替代完全隨機的SYN號。發送完SYN+ACK確認報文之後,清空資源不保存任何狀態信息。直到服務器接到客戶端的最終ACK包,通過Cookie檢驗算法鑑定是否與發出去的SYN+ACK報文序列號匹配,匹配則通過完成握手,失敗則丟棄。當然,前文的高級攻擊中有SYN混合ACK的攻擊方法,則是對此種防禦方法的反擊,其中優劣由雙方的硬件配置決定

tcp_max_syn_backlog則是使用服務器的內存資源,換取更大的等待隊列長度,讓攻擊數據包不至於佔滿所有連接而導致正常用戶無法完成握手。net.ipv4.tcp_synack_retries是降低服務器SYN+ACK報文重試次數,儘快釋放等待資源。這三種措施與攻擊的三種危害一一對應,完完全全地對症下藥。但這些措施也是雙刃劍,可能消耗服務器更多的內存資源,甚至影響正常用戶建立TCP連接,需要評估服務器硬件資源和攻擊大小謹慎設置。

除了定製TCP/IP協議棧之外,還有一種常見做法是TCP首包丟棄方案,利用TCP協議的重傳機制識別正常用戶和攻擊報文。當防禦設備接到一個IP地址的SYN報文後,簡單比對該IP是否存在於白名單中,存在則轉發到後端。如不存在於白名單中,檢查是否是該IP在一定時間段內的首次SYN報文,不是則檢查是否重傳報文,是重傳則轉發並加入白名單,不是則丟棄並加入黑名單。是首次SYN報文則丟棄並等待一段時間以試圖接受該IP的SYN重傳報文,等待超時則判定爲攻擊報文加入黑名單。

首包丟棄方案對用戶體驗會略有影響,因爲丟棄首包重傳會增大業務的響應時間,有鑑於此發展出了一種更優的TCP Proxy方案。所有的SYN數據報文由清洗設備接受,按照SYN Cookie方案處理。和設備成功建立了TCP三次握手的IP地址被判定爲合法用戶加入白名單,由設備僞裝真實客戶端IP地址再與真實服務器完成三次握手,隨後轉發數據。而指定時間內沒有和設備完成三次握手的IP地址,被判定爲惡意IP地址屏蔽一定時間。除了SYN Cookie結合TCP Proxy外,清洗設備還具備多種畸形TCP標誌位數據包探測的能力,通過對SYN報文返回非預期應答測試客戶端反應的方式來鑑別正常訪問和惡意行爲。

清洗設備的硬件具有特殊的網絡處理器芯片和特別優化的操作系統、TCP/IP協議棧,可以處理非常巨大的流量和SYN隊列。

HTTP Flood防禦

HTTP Flood攻擊防禦主要通過緩存的方式進行,儘量由設備的緩存直接返回結果來保護後端業務。大型的互聯網企業,會有龐大的CDN節點緩存內容。

當高級攻擊者穿透緩存時,清洗設備會截獲HTTP請求做特殊處理。最簡單的方法就是對源IP的HTTP請求頻率做統計,高於一定頻率的IP地址加入黑名單。這種方法過於簡單,容易帶來誤殺,並且無法屏蔽來自代理服務器的攻擊,因此逐漸廢止,取而代之的是JavaScript跳轉人機識別方案。

HTTP Flood是由程序模擬HTTP請求,一般來說不會解析服務端返回數據,更不會解析JS之類代碼。因此當清洗設備截獲到HTTP請求時,返回一段特殊JavaScript代碼,正常用戶的瀏覽器會處理並正常跳轉不影響使用,而攻擊程序會攻擊到空處。

DNS Flood防禦

DNS攻擊防禦也有類似HTTP的防禦手段,第一方案是緩存。其次是重發,可以是直接丟棄DNS報文導致UDP層面的請求重發,可以是返回特殊響應強制要求客戶端使用TCP協議重發DNS查詢請求。

特殊的,對於授權域DNS的保護,設備會在業務正常時期提取收到的DNS域名列表和ISP DNS IP列表備用,在攻擊時,非此列表的請求一律丟棄,大幅降低性能壓力。對於域名,實行同樣的域名白名單機制,非白名單中的域名解析請求,做丟棄處理。

慢速連接攻擊防禦

Slowloris攻擊防禦比較簡單,主要方案有兩個。

第一個是統計每個TCP連接的時長並計算單位時間內通過的報文數量即可做精確識別。一個TCP連接中,HTTP報文太少和報文太多都是不正常的,過少可能是慢速連接攻擊,過多可能是使用HTTP 1.1協議進行的HTTP Flood攻擊,在一個TCP連接中發送多個HTTP請求。

第二個是限制HTTP頭部傳輸的最大許可時間。超過指定時間HTTP Header還沒有傳輸完成,直接判定源IP地址爲慢速連接攻擊,中斷連接並加入黑名單。

企業級防禦

互聯網企業防禦DDoS攻擊,主要使用上文的基礎防禦手段,重點在於監控、組織以及流程。

監控需要具備多層監控、縱深防禦的概念,從骨幹網絡、IDC入口網絡的BPS、PPS、協議分佈,負載均衡層的VIP新建連接數、併發連接數、BPS、PPS到主機層的CPU狀態、TCP新建連接數狀態、TCP併發連接數狀態,到業務層的業務處理量、業務連通性等多個點部署監控系統。即使一個監控點失效,其他監控點也能夠及時給出報警信息。多個點信息結合,準確判斷被攻擊目標和攻擊手法。

一旦發現異常,立即啓動在虛擬防禦組織中的應急流程,防禦組織需要囊括到足夠全面的人員,至少包含監控部門、運維部門、網絡部門、安全部門、客服部門、業務部門等,所有人員都需要2-3個備份。流程啓動後,除了人工處理,還應該包含一定的自動處理、半自動處理能力。例如自動化的攻擊分析,確定攻擊類型,自動化、半自動化的防禦策略,在安全人員到位之前,最先發現攻擊的部門可以做一些緩解措施。

除了DDoS到來之時的流程等工作之外,更多的工作是在攻擊到來之前。主要包含CDN節點部署、DNS設置、流程演習等。對於企業來說,具備多個CDN節點是DDoS防禦容量的關鍵指標。當一個機房承擔不住海量數據時,可以通過DNS輪詢的方式,把流量引導到多個分佈節點,使用防禦設備分頭處理。因此DNS的TTL值需要設置得足夠小,能夠快速切換,每個CDN節點的各種VIP設置也需要準備充分。

在虛擬化時代,各種用戶的不同業務共處在相同的物理機平臺,遭受DDoS攻擊的可能性越來越高,而且一個用戶被攻擊可能牽扯到大量的其他用戶,危害被顯著放大,因此防禦顯得尤爲重要。阿里雲的虛擬化業務,平均每天遭受約20起DDoS攻擊,最大流量達到接近20Gbit/s,所有這些攻擊都在15分鐘內自動處理完成,讓客戶遠離DDoS的威脅,專心發展業務。

總地來說,對DDoS防禦,主要的工作是幕後積累。臺上十分鐘,臺下十年功,沒有充分的資源準備,沒有足夠的應急演練,沒有豐富的處理經驗,DDoS攻擊將是所有人的噩夢。

作者魏興國,阿里巴巴集團高級安全專家,主要負責阿里雲虛擬化業務安全,以及集團範圍的網絡安全策略。

發佈了37 篇原創文章 · 獲贊 127 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章