我這里說的“Socket攻擊”是指,用一台或N台肉機進行TCP連接,以此來消耗服務器程序的可用Socket。
看了很多程序或組件的源代碼,一般對客戶端Socket管理是這樣的:
1、用一個TThreadList(只要是線程安全的列表管理都行)來存儲連接成功的Socket:后文統稱“Socket列表”
2、服務器程序預設一個超時時間,比如10秒
3、限制每個IP能連接的Socket數量-----這個可以防止用一台機子寫個程序瘋狂的來連接這種情況
4、單獨開一個線程,每隔幾秒去檢查一下這個 Socket列表,看是否有超時沒有驗證通過的Socket,然后將其這些超時的Socket關閉
實際部署被攻擊出現的問題:
1、服務器檢測到大量的IP(有近5萬個),每個IP都有3,5個連接來連接服務器程序,只連接但不發驗證信息,判定是攻擊
2、Socket列表 好幾萬(我的是64位的程序,WIN SERVER 2008 R2下實際測試可接受11萬多的連接)
3、這些連接不斷連上、斷開,再連上,Socket列表很長,檢測Socket超時的線程工作時間很長導致占用Socket列表的時間很長,把整個服務器程序完全拖慢了。服務器程序處於假死狀態
解決方案
一、加入單位時間能連接的Socket數量,單位時間以兩秒為准效果不錯,比如2秒內只能500個連接,這就可以很好的控制 Socket列表 的長度
二、引入IP黑名單,當一個IP超過10次非法連接后,將其IP拉黑,拉黑后任何從這個IP來的連接直接就Close掉,這樣他怎么來連接也沒影響了
三、改進 Socket列表, 只用一個 Socket列表 由於要處理線程安全的問題,在超多線程的環境下必然導致線程爭用 Socket列表 的情況,這樣多線程的效率就大打折扣了
所以我提出,用 雙Socket列表 的方案。簡單實現如下:
1、定義:fClientGuidMap, fClientGuidMap2: ThHashStringList(這個類是我自己實現的,大家可以用TThreadList之類的);
2、兩個操作函數:
function ExchangeClientGuidMap: ThHashStringList;
procedure ClientGuidMap_AddObject(const S: string; AObject: TObject);
實現如下:
function TROBaseIOCPSuperTcpServer.ExchangeClientGuidMap: ThHashStringList;
begin
ExchangeLock.Acquire;
try
Pointer(fClientGuidMap2) := InterlockedExchangePointer(Pointer(fClientGuidMap), Pointer(fClientGuidMap2));
Result := fClientGuidMap2;
finally
ExchangeLock.Release;
end;
end;
3、檢測線程的代碼就不貼出來了,檢測線程就是先調用 ExchangeClientGuidMap 后,對取得的列表循環判斷其是否超時未驗證,超時就CloseSocket就OK了
經過上面的改造,服務器程序防止攻擊的能力大大提高