2004 年 9 月
概述
在目前以IPv4為支撐的網(wǎng)絡(luò )協(xié)議上搭建的網(wǎng)絡(luò )環(huán)境中,SYN Flood是一種非常危險而常見(jiàn)的DoS攻擊方式。到目前為止,能夠有效防范SYN Flood攻擊的手段并不多,而SYN Cookie就是其中最著(zhù)名的一種。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發(fā)明。在很多操作系統上都有各種各樣的實(shí)現。其中包括Linux。本文就分別介紹一下SYN Flood攻擊和SYN Cookie的原理,更重要的是介紹Linux內核中實(shí)現SYN Cookie的方式。最后,本文給出一種增強目前Linux中SYN Cookie功能的想法。
一 SYN Flood攻擊
SYN Flood攻擊是一種典型的拒絕服務(wù)型(Denial of Service)攻擊。所謂拒絕服務(wù)型攻擊就是通過(guò)進(jìn)行攻擊,使受害主機或網(wǎng)絡(luò )不能夠良好的提供服務(wù),從而間接達到攻擊的目的。
SYN Flood攻擊利用的是IPv4中TCP協(xié)議的三次握手(Three-Way Handshake)過(guò)程進(jìn)行的攻擊。大家知道協(xié)議規定,如果一端想向另一端發(fā)起TCP連接,它需要首先發(fā)送TCP SYN 包到對方,對方收到后發(fā)送一個(gè)TCP SYN+ACK包回來(lái),發(fā)起方再發(fā)送TCP ACK包回去,這樣三次握手就結束了。我們把TCP連接的發(fā)起方叫作"TCP客戶(hù)機(TCP Client)",TCP連接的接收方叫作"TCP服務(wù)器(TCP Server)"。值得注意的是在TCP服務(wù)器收到TCP SYN request包時(shí),在發(fā)送TCP SYN+ACK包回TCP客戶(hù)機前,TCP服務(wù)器要先分配好一個(gè)數據區專(zhuān)門(mén)服務(wù)于這個(gè)即將形成的TCP連接。一般把收到SYN包而還未收到ACK包時(shí)的連接狀態(tài)成為半開(kāi)連接(Half-open Connection)。
在最常見(jiàn)的SYN Flood攻擊中,攻擊者在短時(shí)間內發(fā)送大量的TCP SYN包給受害者,這時(shí)攻擊者是TCP客戶(hù)機,受害者是TCP服務(wù)器。根據上面的描述,受害者會(huì )為每個(gè)TCP SYN包分配一個(gè)特定的數據區,只要這些SYN包具有不同的源地址(這一點(diǎn)對于攻擊者來(lái)說(shuō)是很容易偽造的)。這將給TCP服務(wù)器系統造成很大的系統負擔,最終導致系統不能正常工作。
二 SYN Cookie原理
SYN Cookie是對TCP服務(wù)器端的三次握手協(xié)議作一些修改,專(zhuān)門(mén)用來(lái)防范SYN Flood攻擊的一種手段。它的原理是,在TCP服務(wù)器收到TCP SYN包并返回TCP SYN+ACK包時(shí),不分配一個(gè)專(zhuān)門(mén)的數據區,而是根據這個(gè)SYN包計算出一個(gè)cookie值。在收到TCP ACK包時(shí),TCP服務(wù)器在根據那個(gè)cookie值檢查這個(gè)TCP ACK包的合法性。如果合法,再分配專(zhuān)門(mén)的數據區進(jìn)行處理未來(lái)的TCP連接。
從上面的介紹可以看出,SYN Cookie的原理比較簡(jiǎn)單。到實(shí)際的應用中,它有多種不同的實(shí)現方式。
三 Linux內核中的SYN Cookie實(shí)現
Linux內核中對SYN Flood有很好的防護。以下的討論都是針對Linux2.4.20內核進(jìn)行的。在每一個(gè)sock都有一個(gè)tcp_opt即這個(gè)sock的TCP選項。在tcp_opt其中有一個(gè)tcp_listen_opt,這里存儲的是這個(gè)sock在LISTEN狀態(tài)下時(shí)保存的一些選項,其中有一個(gè)open_request結構的數組,數組長(cháng)度為T(mén)CP_SYNQ_HSIZE(512)。所有這些表示在一個(gè)sock,最多可以同時(shí)開(kāi)啟512個(gè)半開(kāi)連接(這是在不考慮其他約束條件時(shí)的最大值,實(shí)際情況中不會(huì )達到這個(gè)值)。當這個(gè)數組滿(mǎn)了時(shí),新來(lái)的open_request會(huì )頂替掉一個(gè)老的open_request。這樣,即使沒(méi)有啟動(dòng)SYN Cookie,也能夠在SYN Flood發(fā)生時(shí)保護系統免于癱瘓。問(wèn)題是這種處理方法會(huì )在面對SYN Flood攻擊時(shí)丟掉正常的TCP連接請求。SYN Cookie的作用恰恰是保證在面對SYN Flood攻擊時(shí),一方面能夠拒絕非法的TCP連接請求,一方面正常連接可以被建立。
Linux內核對TCP流程的處理主要在tcp_ipv4.c文件中的函數實(shí)現。具體的,當處理TCP SYN包時(shí),系統進(jìn)入tcp_v4_conn_request函數。其中調用cookie_v4_init_sequence生成一個(gè)ISN(Initial Sequence Number)。Linux內核把它作為SYN Cookie流程中的cookie。
cookie_v4_init_sequence函數在syncookies.c文件中定義,它又調用random.c文件中的secure_tcp_syn_cookie函數。cookie的實(shí)質(zhì)計算是在這個(gè)函數中進(jìn)行的。
在random.c文件里給出secure_tcp_syn_cookie函數的定義之前給出兩個(gè)宏,它們的定義分別為
|
COOKIEBITS表示cookie的比特長(cháng)度;COOKIEMASK是一個(gè)COOKIEBITS長(cháng)的比特串,所有比特都是1。
還有兩個(gè)比特串,被定義成一個(gè)__u32的二維數組
|
其中所有的比特值在secure_tcp_syn_cookie中被隨機的賦予,用get_random_bytes函數。它們成為制作cookie的密鑰。這兩個(gè)被隨機產(chǎn)生的比特串是整個(gè)SYN Cookie實(shí)現方案的關(guān)鍵。另外還有一個(gè)開(kāi)關(guān)syncookie_init控制對這兩個(gè)密鑰的改動(dòng)。
還需要指出,在文件syncookies.c中定義有一個(gè)__u16組成的表static __u16 const msstab[],這個(gè)表中保存的是一些可能的MSS(Maximum Segment Size)值。
secure_tcp_syn_cookie函數的返回值就是計算得到的ISN值,即cookie。為了描述方便,我們給出如下定義:
|
sseq := ntohl(skb->h.th->seq) 這里的skb是攜帶TCP SYN的那個(gè)skb
count1 := jiffies/(HZ*60) 當前時(shí)間的分鐘值
data1 := msstab
從前往后最后一個(gè)小于skb中攜帶的MSS值的值的索引(值得注意的是兩個(gè)密鑰在第一次被初始化后,就不會(huì )再有改動(dòng),直到系統重新啟動(dòng)。因此可以認為它是一個(gè)常值。)
有了上面的定義我們可以得到cookie等于
|
這個(gè)isn被賦予返回的TCP SYN+ACK包中,作為其中的ISN值。這就是cookie 的產(chǎn)生過(guò)程。在這個(gè)過(guò)程中,沒(méi)有在本地為這個(gè)連接請求分配任何存儲空間。
在TCP服務(wù)器收到TCP ACK包時(shí),相應的要進(jìn)行SYN Cookie的檢查。這個(gè)檢查過(guò)程在函數tcp_v4_hnd_req中的cookie_v4_check函數開(kāi)始。cookie_v4_check調用cookie_check函數,cookie_check函數調用check_tcp_syn_cookie函數。
check_tcp_syn_cookie函數在random.c中定義,是與前面介紹的
secure_tcp_syn_cookie函數對應的函數,檢查從TCP ACK中提取出的ISN值。
在check_tcp_syn_cookie中假定ISN的值如下
|
這里的A、B都是根據當前這個(gè)skb中的地址信息和syncookie_secret算出來(lái)的;sseq是根據這個(gè)skb中的seq值算出的。
有了上面這些值,TCP服務(wù)器就可以反算出count2和data2。理論上來(lái)說(shuō),只要這個(gè)isn是原來(lái)那個(gè)isn,應該有
|
但是這種結論僅僅是一個(gè)理論情況。因為在TCP服務(wù)器端并沒(méi)有保存原來(lái)的count1和data1,因此不能直接進(jìn)行比較。TCP服務(wù)器采取的方法是:
1)計算出當前的分鐘值
count3 := jiffies/(HZ*60)
用count3與count2比較,如果差值超過(guò)COUNTER_TRIES(4)分鐘,則認為這 個(gè)ACK包不合法。
2)看data2是不是一個(gè)合法的msstab的索引,也就是說(shuō)是不是小于NUM_MSS, 即(sizeof(msstab)/sizeof(msstab[0]) - 1)。如果小于,則認為這個(gè)ACK 合法,否則認為非法。
上面介紹的就是Linux內核Linux2.4.20中對SYN Cookie的實(shí)現方式。下面討論一下它的合理性。希望得到的結論是這種方案可以有效的實(shí)現一般TCP的連接,同時(shí)可以防止SYN Flood攻擊。
從上面的介紹來(lái)說(shuō),合法的TCP連接請求一定可以通過(guò)SYN Cookie流程。 另一方面我們看SYN Cookie在系統受到各種SYN Flood攻擊時(shí)會(huì )采取的行為。 最一般的SYN Flood攻擊方式是攻擊者作為T(mén)CP客戶(hù)機發(fā)送大量TCP SYN包而不再發(fā)送其他的包。這時(shí)SYN Cookie會(huì )為每個(gè)SYN包計算出相應的ISN值,并返回SYN+ACK包,而在本地將不分配任何存儲空間,因此不會(huì )被成功攻擊。
根據SYN Cookie的原理,攻擊者有可能直接發(fā)送大量ACK包。這時(shí)SYN Cookie提取出每個(gè)包的isn值,并假定它有下面的格式
|
反算出count和data。
因為攻擊者并不知道這里的A和B,因此經(jīng)過(guò)反算出的count和data幾乎不可能都合理,因此TCP服務(wù)器也幾乎不可能為這些ACK包分配存儲空間,這也就說(shuō)明了SYN Cookie達到起到了抵擋SYN Flood攻擊的作用。
四 SYN Cookie Firewall
從上面的介紹可以看到,Linux內核中的SYN Cookie機制主要的功能是防止本機遭受SYN Flood攻擊的,但是在很多情況下,僅僅實(shí)現這樣的SYN Cookie機制是不夠的。如果我們要考慮的是一個(gè)網(wǎng)關(guān)模式的防火墻,它不僅要保護本機免受各種網(wǎng)絡(luò )攻擊,還要保護它后面的所有對外有開(kāi)放TCP端口的主機免受這些攻擊。比如一個(gè)局域網(wǎng)中有個(gè)服務(wù)器開(kāi)放了FTP服務(wù)給外界,這個(gè)服務(wù)器主機就有可能遭受到來(lái)自互聯(lián)網(wǎng)上的SYN Flood攻擊。而這時(shí)的防火墻會(huì )將所有的攻擊SYN包轉發(fā)給受害主機。
一種杜絕這種情況的方法是SYN Cookie Firewall。它是SYN Cookie的一種擴展形式??偟膩?lái)說(shuō),它是利用原來(lái)SYN Cookie的原理在內網(wǎng)和外網(wǎng)之間實(shí)現TCP三次握手過(guò)程的代理(proxy)的機制。
為了方便描述,我們假定一個(gè)外在的TCP客戶(hù)機C希望通過(guò)防火墻F連接到局域網(wǎng)中的一個(gè)TCP服務(wù)器S。
在防火墻收到來(lái)自外網(wǎng)的SYN包時(shí),它并不直接進(jìn)行轉發(fā),而是緩存在本地,再按照原來(lái)SYN Cookie的機制制作好一個(gè)針對這個(gè)SYN包的SYN+ACK包,注意,這個(gè)SYN+ACK包中的ack順序號為特制的cookie值c,更重要的是這個(gè)包的的源地址被偽造成了S的地址(為了描述方便,我們這里暫時(shí)不考慮NAT等其他因素)。這樣C會(huì )接收到這個(gè)SYN+ACK包,并認為是從S反饋回來(lái)的。于是C再響應一個(gè)ACK包,并認為與S的TCP連接已經(jīng)建立起來(lái)。這時(shí)防火墻F收到這個(gè)ACK包,按照前面的描述的SYN Cookie原理來(lái)檢查這個(gè)ACK中的ack順序號。如果認為合法,F將本地緩存的來(lái)自C的SYN包發(fā)送給S,這時(shí)S會(huì )響應一個(gè)SYN+ACK包到C,其中也攜帶一個(gè)seq號, 我們設為c`。當然這個(gè)包不會(huì )到達C,而是由防火墻F截取,F根據這個(gè)包中的序列號等信息,造一個(gè)ACK包響應到S。這時(shí)的情況是:C認為自己已經(jīng)與S建立了TCP連接;S認為自己與C建立了TCP連接。以后的TCP數據內容可以直接穿過(guò)防火墻F,在S和C之間交互。

上圖是SYN Cookie Firewall的工作原理,它相當于在TCP Server與TCP Client之間實(shí)現了對三次握手協(xié)議的代理。第一次"三次握手"在TCP Client與防火墻之間進(jìn)行,第二次"三次握手"在防火墻與TCP Server之間。在第一次"三次握手"時(shí)使用前面介紹的SYN Cookie流程。有一個(gè)問(wèn)題在進(jìn)行兩次"三次握手"時(shí)出現了:如圖所示,進(jìn)行第一次"三次握手"后,TCP Client認為后續數據包的seq值從c+1開(kāi)始,而進(jìn)行第二次"三次握手"后,TCP Server認為后續發(fā)來(lái)的數據包的seq值從c`+1開(kāi)始, c是cookie,c`是TCP Server隨機產(chǎn)生的。c和c`幾乎不可能相等,也就是說(shuō)在完成上面的兩個(gè)"三次握手"后,如果不進(jìn)行其他操作,后續從TCP Client到TCP Server的數據包都將被認為順序號不對而被丟掉。一種補救方法就是在防火墻本地保存一個(gè)值δ δ = |c - c`|
利用這個(gè)差值,在每個(gè)數據包經(jīng)過(guò)防火墻時(shí),將其seq值修改一下,這樣,后續的數據流量可以完美地在TCP Server和TCP Client之間傳輸了。
總結
現在普遍使用的IPv4協(xié)議帶有很多安全上的問(wèn)題,其中面對SYN Flood攻擊的軟弱就是一點(diǎn)。在不改變TCP三次握手流程的情況下,TCP Server幾乎不可能有效的防范SYN Flood的攻擊。要保證完全防范SYN Flood,必須修改三次握手協(xié)議。SYN Cookie是一種很有效的方法。它的思想比較簡(jiǎn)單,主要是如何具體的實(shí)現,Linux系統也提供了一種實(shí)現。作者通過(guò)研讀Linux2.4.20內核中的代碼,基本了解了Linux內核中實(shí)現SYN Cookie的手段,將其總結成文字,與對SYN Cookie同樣感興趣的朋友分享、交流。
聯(lián)系客服