TCP的syn-cookie以及SHA1摘要演算法的實現

來源:互聯網
上載者:User

linux核心中實現了syn-cookie,可以有效阻止syn-flood攻擊,syn-cookie理論上很簡單,就是在伺服器接收到用戶端的syn包時並不分配任何記憶體空間,而是巧妙的選擇伺服器的isn值傳給用戶端,isn本地也並不儲存(本地不儲存任何東西),然後用戶端發來synack的ack確認包後,從該確認包中取出確認號,然後利用確認號是上一次序號加1的特性來得到原來的伺服器發給用戶端的synack包的序號,然後用相同的源,目的地址/連接埠資訊用相同的演算法計算一個數和得到的原始序號比較,如果一樣就說明串連合法,否則不合法,在傳輸中使用逾時機制。linux的實現大體上和理論的類似,只是使用sha1演算法得到的isn中儲存了mss值,這是個額外的收穫,另外還限制了兩次握手之間的最短時限:

__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)

{

struct tcp_sock *tp = tcp_sk(sk);

int mssind;

const __u16 mss = *mssp;

tp->last_synq_overflow = jiffies;

for (mssind = 0; mss > msstab[mssind + 1]; mssind++);

*mssp = msstab[mssind] + 1;

NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESSENT); //以下調用Function Compute摘要,最終作為伺服器端序號發送

return secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr, skb->h.th->source, skb->h.th->dest,

ntohl(skb->h.th->seq), jiffies / (HZ * 60), mssind);

}

static __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport, __u32 sseq, __u32 count, __u32 data)

{

//count

return (cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq + (count

((cookie_hash(saddr, daddr, sport, dport, count, 1) + data) & COOKIEMASK));

}

static inline int cookie_check(struct sk_buff *skb, __u32 cookie)

{

__u32 seq;

__u32 mssind;

seq = ntohl(skb->h.th->seq)-1;

mssind = check_tcp_syn_cookie(cookie,

skb->nh.iph->saddr, skb->nh.iph->daddr,

skb->h.th->source, skb->h.th->dest,

seq, jiffies / (HZ * 60), COUNTER_TRIES);

return mssind

}

static __u32 check_tcp_syn_cookie(__u32 cookie, __u32 saddr, __u32 daddr, __u16 sport, __u16 dport, __u32 sseq, __u32 count, __u32 axdiff)

{

__u32 diff;

cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;

//經過上面計算以後,cookie剩下了高8位的那時的分鐘數和由地址,連接埠,時間算出來的一個sha摘要值以及一個附加的mss值,以下的diff計算得到目前時間和通過cookie得到的時間的差值,cookie >> COOKIEBITS得到cookie的高8位,最後的一個&操作屏掉了不相關的高位,但是有一個問題,見下面。

diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);

if (diff >= maxdiff) //這個maxdiff就是COUNTER_TRIES,該值是通過tcp的逾時機制計算出來的

return (__u32)-1;

//通過以下的減法就得到了當初的mss值,diff肯定小於maxdiff,也就是4,那麼count減去這個差值就是原來的時間,以分鐘計時。

return (cookie - cookie_hash(saddr, daddr, sport, dport, count - diff, 1)) & COOKIEMASK;

}

以上的演算法並不是十全十美的,問題出在diff的計算上,如果當前的count值和過去產生cookie時的count值差很大,比如現在的count是11111111111111111111111100000011,原來的是00000000000000000000000000000001,這樣通過diff的公式計算得到2,完全符合,然後計算下面的cookie_hash,此時如果sha又碰撞了,那麼就會得到錯誤的結果,但是即使出錯也沒有什麼大問題,出錯的幾率畢竟很小很小,因此syn-flood攻擊還是無法成功,再者說了,之所以這裡的diff檢查不嚴格,一個後面還有一個sha計算,第二是tcp本身就是逾時機制,相隔那麼長的時間逾時早就過了,因此根本就不會有那樣的包回來。另外即使不碰撞,萬一碰到由於count或者別的addr或者port等改變而引起了sha摘要的改變,但是改變的很小,被cookie減去之後仍然是合法的範圍內的值,這樣也會通過驗證,不過大可不必擔心這樣的情況,出錯率很低,摘要離得很近的機率也很低,因此這樣的驗證是可信的,設計系統時千萬不要為了萬無一失而損失效率。

並不是只要設定了syn-cookie,每次有用戶端串連都要進行驗證,而是只有在一定情況下才開啟,因為syn-cookie是很耗時並且違背tcp原則的,且看下面的程式碼片段,這是在tcp服務端進行三向交握期間回複syn包時執行的程式碼片段:

if (inet_csk_reqsk_queue_is_full(sk) && !isn) {

#ifdef CONFIG_SYN_COOKIES

if (sysctl_tcp_syncookies) {

want_cookie = 1;

} else

#endif

goto drop;

}

...

if (want_cookie) {

isn = cookie_v4_init_sequence(sk, skb, &req->mss);

}

只有在伺服器的accept_queue滿載時才會啟用,可以看出懶惰啟用對效率提升很有效,並且還可以有效防止syn-flood攻擊,你可以隨便攻擊,這個linux不管,linux絲毫不會畏懼dos攻擊,也不會提前採取任何行動,一切就像什麼也沒有發生一樣,但是一旦攻擊開始影響系統,這裡就是接收隊列已經爆滿,linux就要應付了,啟動應急預案,這裡就是syn-cookie。

那麼linux中是怎麼實現syn-cookie的呢?其實是用sha1摘要演算法計算的,接下來就看一下這個演算法:

#define SHA_DIGEST_WORDS 5 //sha1的初始摘要一共5個32位空間

#define SHA_WORKSPACE_WORDS 80 //計算一個摘要需要80個32位的緩衝區空間

static __u32 syncookie_secret[2][16-3+SHA_DIGEST_WORDS]; //一共2*21*4位元組的空間

static u32 cookie_hash(u32 saddr, u32 daddr, u32 sport, u32 dport, u32 count, int c)

{

__u32 tmp[16 + 5 + SHA_WORKSPACE_WORDS];

memcpy(tmp + 3, syncookie_secret[c], sizeof(syncookie_secret[c]));

tmp[0] = saddr;

tmp[1] = daddr;

tmp[2] = (sport

tmp[3] = count;

//從tmp的21位移處開始填充sha的字空間,填充的內容是tmp的既有的前16個元素

//一共16+5+80個32位空間,前16個是既有資料空間,接著5個是sha輸出緩衝區,注意這5個空間已經被填充了部分syncookie_secret,因為一個syncookie_secret元素的長度是18,並且從第三個tmp空間開始填充,故而一直延展到第21個空間,從第21個空間往後就成了sha計算緩衝區了。

sha_transform(tmp + 16, (__u8 *)tmp, tmp + 16 + 5);

return tmp[17]; //對於tcp的syn-cookie,只需要挑一個就可以了

}

sha演算法核心,digest參數為摘要資訊指標,其實就需要其前5個無符號32位元據,in是需要計算摘要的資料,最後的W是計算空間緩衝區,由參數digest和in以及動態計算被填充:

void sha_transform(__u32 *digest, const char *in, __u32 *W)

{

__u32 a, b, c, d, e, t, i;

for (i = 0; i

W[i] = be32_to_cpu(((const __be32 *)in)[i]);

for (i = 0; i

W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1);

a = digest[0]; //這些操作是賦值操作,將資料緩衝區賦值給臨時變數

b = digest[1]; //一共是5個32位緩衝區

c = digest[2]; //對於syn-cookie來講,這個digest就是調用函數的tmp數組的從16下標開始的,已經被覆蓋了syncookie_secret

d = digest[3];

e = digest[4]; //以下開始計算,分段計算

for (i = 0; i

t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i];

e = d; d = c; c = rol32(b, 30); b = a; a = t;

}

for (; i

t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i];

e = d; d = c; c = rol32(b, 30); b = a; a = t;

}

for (; i

t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i];

e = d; d = c; c = rol32(b, 30); b = a; a = t;

}

for (; i

t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i];

e = d; d = c; c = rol32(b, 30); b = a; a = t;

}

digest[0] += a; //輸出資料

digest[1] += b;

digest[2] += c;

digest[3] += d;

digest[4] += e;

}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.