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