在前面的文章我們說到過,可以採用長度+數據的格式定義包結構;但是如果長度定義的特別大,那麼緩衝區必然會存儲不下,而引起宕機或其他問題。這便是一種socket攻擊。
除此之外,如果定義的長度和實際長度不匹配,比如實際長度爲5,定義的長度爲6,那麼讀取的時候可能會把下一個數據包的一部分讀取到,造成以後的數據不能正常解析;這也是一種socket攻擊。
可以通過以下幾個方面處理socket攻擊:
1.定義字節的最大長度,如果字節流大於Max,跳過所有字節流
// 防止Socket攻擊
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
2.但是當跳過所有字節流後,可用包頭定位到下一個正確的數據包開始的地方,所以標準包結構還應該有個起始的包頭標誌。跳過最大長度後,循環讀取數據包一個字節,判斷是否是包頭;如果不是則跳過繼續讀,直到讀到包頭,跳出循環,然後纔可以繼續解析;但是這個過程可能會在讀的過程中長度小於最小包結構長度了,這時候要直接返回null,等待下次數據到來後再解析;
具體代碼如下:
package com.cn.codc;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import com.cn.constant.ConstantValue;
import com.cn.model.Response;
/**
* response解碼器
* <pre>
* 數據包格式
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* | 包頭 | 模塊號 | 命令號 | 狀態碼 | 長度 | 數據 |
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* </pre>
* 包頭4字節
* 模塊號2字節short
* 命令號2字節short
* 長度4字節(描述數據部分字節長度)
*
-
*
*/
public class ResponseDecoder extends FrameDecoder{
/**
* 數據包基本長度
*/
public static int BASE_LENTH = 4 + 2 + 2 + 4;
@Override
protected Object decode(ChannelHandlerContext arg0, Channel arg1, ChannelBuffer buffer) throws Exception {
//可讀長度必須大於基本長度
if(buffer.readableBytes() >= BASE_LENTH){
//防止字節流攻擊
if(buffer.readableBytes()>2048){
//清除緩存中的數據
buffer.skipBytes(buffer.readableBytes());
}
//記錄包頭開始的index
int beginReader ;
while(true){
beginReader = buffer.readerIndex();
//標記當前索引
buffer.markReaderIndex();
//判斷包頭是否是當前的包頭
//因爲清除了數據之後,因爲可能會出現分包截斷的現象,下次進來的時候,
//可能不是開頭,所以不能知道哪個是長度,哪個是數據,所以需要包頭,
//只有當時讀到包頭的時候才繼續往下走。
if(buffer.readInt() == ConstantValue.FLAG){
break;
}
//未讀到包頭超過了一個字節
buffer.resetReaderIndex();
//因爲可能讀一個int之後,略過了包頭,因爲可能包頭在第一個字節處
//所以這裏選擇繼續往下讀一個字節
buffer.readByte();
//可能出現極端的情況,長度又變得不滿足
if(buffer.readableBytes()<BASE_LENGTH){
return null
}
}
//模塊號
short module = buffer.readShort();
//命令號
short cmd = buffer.readShort();
//狀態碼
int stateCode = buffer.readInt();
//長度
int length = buffer.readInt();
if(buffer.readableBytes() < length){
//還原讀指針
buffer.readerIndex(beginReader);
return null;
}
byte[] data = new byte[length];
buffer.readBytes(data);
Response response = new Response();
response.setModule(module);
response.setCmd(cmd);
response.setStateCode(stateCode);
response.setData(data);
//繼續往下傳遞
return response;
}
//數據包不完整,需要等待後面的包來
return null;
}
}