在上一篇文章《DDoS攻防戰 (二) :CC攻擊工具實現與防禦理論》中,筆者闡述了一個防禦狀態機,它可用來抵禦來自應用層的DDoS攻擊,但是該狀態機依賴一個能應對大量條目快速增刪的ip黑白名單防火牆,我們目前並沒有發現很好的開源實現以供我們使用。
·實現方案選擇:
硬體實現或者軟體實現?
在面對諸如大量畸形包這樣的攻擊時,硬體實現將會是非常好的選擇,這是因為在進行此型別的封包過濾時,系統需要記憶的狀態很少(對於FPGA、ASIC諸多硬體實現方案來講,記憶元件的成本決不可忽視,暫存器與靜態RAM都非常昂貴,所以當需要記憶的資訊很少時,純硬體方案的速度優勢使得其完勝軟體方案)。
但是,當狀態機需要處理龐大的記憶資訊時,我們就需要選擇廉價的儲存器——動態隨機儲存器(如SDRAM中的DDR3)來作為系統狀態機的儲存介質,以降低系統的成本和複雜度。這時,軟體實現更勝一籌。儘管純硬體實現的速度會比軟體的方式高出很多,但我們也從第一篇文章《DDoS攻防戰 (一) : 概述》中lvs效能的測試結果中看到,軟體實現的、作為伺服器前端均衡排程器的lvs,效能理想並且能勝任實際生產環境中的、龐大的使用者請求處理,可見,如果設計合理,軟體實現的效能無需過多擔憂。
最終,我們決定採用軟體的方法來實現所需的ip黑白名單模組。
·最終系統鳥瞰:
筆者花費大約二十天的時間,使用C語言實現了這一模組,其中,核心空間的核心程式碼約2300行,使用者空間管理工具的程式碼總行數約為700行。下為系統的鳥瞰:
·使用者空間管理工具fripadm,通過ioctl與工作於核心態的frdev模組進行通訊
·frdev維護兩個double_hash_table的例項,並提供了一個掛在NF_INET_PRE_ROUTING的鉤子函式,其通過操作這兩個double_hash_table的例項以分別實現ip黑名單、白名單的功能
·frdev通過核心中裝置驅動的ioctl機制,向使用者空間提供這兩個double_hash_table例項的操作函式,而我們的使用者空間管理工具fripadm正是基於此而實現的
下面是核心態的主要資料結構與其對應的操作函式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
struct fr_ip_hash_array的功能: 精確ip查詢; 模糊ip查詢; 自定義hash表的長度; 自定義hash function,其輸入雜湊隨機數為rnd; 維護精確ip的雜湊表; 維護模糊ip的連結串列; 維護精確ip與模糊ip的諸統計資訊; ip字元描述語法: /* ips_syntax : RE digit =: [0-9] num =: (digit){1,3} atom =: num | (num'-'num) | '*' ip =: atom '.' atom '.' atom '.' atom ips =: (ip ' ')+ */ // ret 0 success,otherwise syntax error // "1-220.*.100.33 1-220.*.100.33 1-220.*.100.33" struct fr_ip_hash_array的方法: fr_ip_hash_array_malloc / fr_ip_hash_array_destroy fr_ip_hash_array_insert_ip :增加一條精確ip記錄 fr_ip_hash_array_insert_blurip_ptr :增加一條模糊ip記錄(以指標引用的方式) fr_ip_hash_array_delete_ip fr_ip_hash_array_delete_blurip_ptr fr_ip_hash_array_delete_ip_randomly :隨機地刪除指定數量的精確ip fr_ip_hash_array_insert_ip_bystrings :通過字串的表述方式,向fr_ip_hash_array增加精確ip或者模糊ip fr_ip_hash_array_delete_ip_bystrings fr_ip_hash_array_find_bool :查詢給定的ip是否在已儲存的模糊ip範圍之內或者精確ip的雜湊表之中 fr_ip_hash_array_find_ip_bool :查詢給定的ip是否在精確ip的雜湊表之中 fr_ip_hash_array_find_ip_bystrings_bool |
·為什麼使用雙雜湊表緩衝?
請考慮如下場景:
情況1:來自應用層的DDoS攻擊常常是瞬間湧入大量非法ip請求,例如數萬個非法ip,所以,對於防火牆黑白名單功能的要求至少有如下:能在很短的時間內更新大量資料項,且不能造成系統服務停頓。
分析:如果只使用一個全域性的雜湊表,當在短時間內進行大量的資料項增刪時,例如,成千上萬個,此時,即使採用多把讀寫鎖分割雜湊表的策略,對共享資源的競爭也依然將嚴重影響系統響應速度,嚴重時系統可能會停頓或者更糟,對於生產環境中的高負載伺服器,這是無法容忍的。
解決:以空間換時間
採用雙雜湊表緩衝的策略,將系統共享資源的競爭熱點壓縮至兩個hash表指標主備切換的極短時間內,此方法能顯著降低系統在大量資料項更新時共享資源的競爭。
系統查詢將會直接訪問master指向的fr_ip_hash_array,而使用者的更新操作將直接針對mirror所指向的另一個fr_ip_hash_array例項,直到switch_mirror_update操作的執行,master將被瞬間“更新”。這是其主要的工作特點。
對於SMP與多佇列網路卡的系統,可採用如下策略:多數cpu核心專門負責處理核心的softirq任務,剩下的少數cpu負責進行雙雜湊表的更新、切換與重建等操作。這樣可提高系統對攻擊的快速防禦響應。
但此方案將使得系統需要維護兩個互為映象的雜湊表,這將加重系統記憶體的讀寫負擔。
具體實現細節如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct fr_ip_double_hash的成員: struct fr_ip_hash_array * master_ptr; rwlock_t master_lock; struct fr_ip_hash_array * mirror_ptr; rwlock_t mirror_lock; struct fr_ip_double_hash的方法: fr_ip_double_hash_malloc / fr_ip_double_hash_destroy fr_ip_double_hash_mirror_insert_ip :只針對mirror的insert ip操作 fr_ip_double_hash_mirror_insert_blurip_ptr fr_ip_double_hash_mirror_insert_bystrings fr_ip_double_hash_mirror_delete_ip fr_ip_double_hash_mirror_delete_blurip_ptr fr_ip_double_hash_mirror_delete_bystrings fr_ip_double_hash_mirror_delete_ip_randomly fr_ip_double_hash_mirror_delete_all :刪除mirror中所有的ip記錄,即所有的精確ip和模糊ip記錄 fr_ip_double_hash_switch_mirror_update :將mirror與master互換,並更新master至mirror(此時的mirror即為之前的master) fr_ip_double_hash_rebuild :將雙雜湊表重建,可指定新的hash function、rnd以及hash表的長度,這將解決hash表查詢效率低下的問題,以防禦外界針對hash表的攻擊。當然,在重建之後,原有的諸多ip條目不會丟失。 fr_ip_double_hash_find_bool fr_ip_double_hash_find_bystrings_bool |
掛到協議棧上的鉤子函式:
在模組初始化函式fr_ip_dev_init的最後,即當兩個雙雜湊表例項(分別用作黑名單與白名單)初始化成功、fedev裝置註冊成功之後,其將會執行nf_register_hook,將指定的鉤子函式fr_nf_hook_sample掛到NF_INET_PRE_ROUTING之上。
fr_nf_hook_sample的主要處理程式碼如下:
1 2 3 4 5 6 |
if(fr_ip_double_hash_find_bool(double_hash_white_ptr,sip)) return NF_ACCEPT; else if(fr_ip_double_hash_find_bool(double_hash_ptr,sip)) return NF_DROP; else return NF_ACCEPT; |
其中,double_hash_white_ptr指向白名單fr_ip_double_hash例項,double_hash_ptr則指向黑名單fr_ip_double_hash例項,由於支援模糊ip匹配,故,上述程式碼使得對源ip過濾的“通”、“堵”策略皆可使用。
核心空間frdev的ioctl處理函式:
目前,ioctl支援來自使用者空間的如下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//黑名單操作 /* in-black ip */ #define FR_IP_IOCTL_TYPE_FIND 1 #define FR_IP_IOCTL_TYPE_FIND_BYSTRINGS 2 // * 輸入ip字元查詢 #define FR_IP_IOCTL_TYPE_MIRROR_INSERT_IP 3 #define FR_IP_IOCTL_TYPE_MIRROR_INSERT_BYSTRINGS 4 // * #define FR_IP_IOCTL_TYPE_MIRROR_DELETE_IP 5 // * #define FR_IP_IOCTL_TYPE_MIRROR_DELETE_IP_RANDOMLY 6 //* #define FR_IP_IOCTL_TYPE_MIRROR_DELETE_BYSTRINGS 7 // * #define FR_IP_IOCTL_TYPE_MIRROR_DELETE_ALL 8 //* #define FR_IP_IOCTL_TYPE_SWITCH_MIRROR_UPDATE 9 // * #define FR_IP_IOCTL_TYPE_REBUILD 10 // * #define FR_IP_IOCTL_TYPE_COPY_HASH_STRUCT 11 // * #define FR_IP_IOCTL_TYPE_DUMP 12 //* 輸出雙雜湊表的分佈情況與統計資訊 #define FR_IP_IOCTL_TYPE_MIRROR_INSERT_IP_BINS 13 //* 一次增加大量精確ip,以二進位制的方式輸入 #define FR_IP_IOCTL_TYPE_MIRROR_DELETE_IP_BINS 14 //* //白名單操作 /* in-white ip */ #define FR_IP_IOCTL_TYPE_WHITE_FIND 101 #define FR_IP_IOCTL_TYPE_WHITE_FIND_BYSTRINGS 102 // * #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_INSERT_IP 103 #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_INSERT_BYSTRINGS 104 // * #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_DELETE_IP 105 #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_DELETE_IP_RANDOMLY 106 // * #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_DELETE_BYSTRINGS 107 // * #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_DELETE_ALL 108 //* #define FR_IP_IOCTL_TYPE_WHITE_SWITCH_MIRROR_UPDATE 109 // * #define FR_IP_IOCTL_TYPE_WHITE_REBUILD 110 // * #define FR_IP_IOCTL_TYPE_WHITE_COPY_HASH_STRUCT 111 // * #define FR_IP_IOCTL_TYPE_WHITE_DUMP 112 //* #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_INSERT_IP_BINS 113 //* #define FR_IP_IOCTL_TYPE_WHITE_MIRROR_DELETE_IP_BINS 114 //* |
上述各功能的具體實現請閱讀frdev原始碼中的fr_ip_dev_ioctl_routine函式。
使用者空間管理工具:fripadm
第一步,實現fripadm_black_in_exe與fripadm_white_in_exe,是分別管理黑白名單的工具,不過,較為簡陋,第二步,使用shell指令碼對其進行二次封裝得到fripadm_black_in.sh與fripadm_white_in.sh這兩個較使用者友好的工具。
fripadm為使用者空間的C語言開發者們提供瞭如下API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//針對ip黑名單的操作 double_hash_find_bystrings(int fd,char * str, unsigned int size) //其中fd為frdev的fd——檔案描述符 double_hash_mirror_insert_bystrings(int fd,char * str, unsigned int size) double_hash_mirror_insert_bins(int fd,char * str, unsigned int size) double_hash_mirror_delete_bystrings(int fd,char * str, unsigned int size) double_hash_mirror_delete_bins(int fd,char * str, unsigned int size) double_hash_mirror_delete_ip_randomly(int fd,unsigned int size) double_hash_mirror_delete_all(int fd) double_hash_switch_mirror_update(int fd) double_hash_rebuild(int fd,unsigned int modular, unsigned int rnd) double_hash_dump(int fd) //針對ip白名單的操作 double_hash_white_find_bystrings(int fd,char * str, unsigned int size) //其中fd為frdev的fd——檔案描述符 double_hash_white_mirror_insert_bystrings(int fd,char * str, unsigned int size) double_hash_white_mirror_insert_bins(int fd,char * str, unsigned int size) double_hash_white_mirror_delete_bystrings(int fd,char * str, unsigned int size) double_hash_white_mirror_delete_bins(int fd,char * str, unsigned int size) double_hash_white_mirror_delete_ip_randomly(int fd,unsigned int size) double_hash_white_mirror_delete_all(int fd) double_hash_white_switch_mirror_update(int fd) double_hash_white_rebuild(int fd,unsigned int modular, unsigned int rnd) double_hash_white_dump(int fd) |
fripadm_black_in_exe、fripadm_white_in_exe、fripadm_black_in.sh與 fripadm_white_in.sh的具體實現請參看frdev原始碼。
最終系統測試:
按照README所述的過程,編譯、安裝完畢frdev裝置後,便可進行如下測試:
原本被black所DROP的資料包,在更新了white的ip條目後,被white所ACCEPT,上圖紅線標出了資料包被截斷的icmp_seq的區間。
關於frdev的陳述到此為止。
最近筆者在閱讀《白帽子講Web安全》這本書時,發現了雅虎公司用於防護應用層DDoS攻擊的系統Yahoo Detecting System Abuse,yahoo為此係統申請了專利保護。下面是關於這個系統的描述:
(Patent N0.: US 7,533,414 B1 資料來源 http://patentimages.storage.googleapis.com/pdfs/US7533414.pdf)
A system continually monitors service requests and detects service abuses.
First, a screening list is created to identify potential abuse events. A screening list includes event IDs and associated count values. A pointer cyclically selects entries in the table,advancing as events are received.
An incoming event ID is compared with the event IDs in the table. If the incoming event ID matches an event ID in the Screening list,the associated count is incremented. Otherwise, the count of a selected table entry is decremented. If the count value of the selected entry falls to Zero, it is replaced With the incoming event.
Event IDs can be based on properties of service users,such as user identifications, or of service request contents,such as a search term or message content. The screening list is analyzed to determine whether actual abuse is occurring.
大概思路如下:
此係統通過維護一個篩選表來得到使用者的請求頻率,以判斷其是否存在service abuse,然採取相關措施,例如BLOCK。
這種防禦思想,與我們之前所提出的防禦狀態機有著異曲同工之妙。筆者認為這是必然的。
前面的文章已經提過,DDoS攻擊存在的主要原因之一是網路服務的開放性,我們不可能從下層來解決這樣的問題(因為服務的可用性是第一要求),只能從上層分析解決.
而應用層已經處於協議棧的最高層,所以,要防禦應用層DDoS攻擊,只能從應用層以上來尋找解法,故,在這種情況下,除了藉助資料統計分析,難道還會有更好的方法麼?
在實現frdev的過程中,藉助網際網路解決了很多問題,所以,如果您需要frdev的原始碼,請在下方留下郵箱 :)
如果有小夥伴想要一起來增強frdev的功能,熱烈歡迎 :)
通訊郵箱:yunthanatos@163.com