Netty 快速入门系列 - Chapter 8 数据包协议【第二十二讲】Socket攻击及防护
2018-03-27 08:18
941 查看
1. Socket攻击
把长度定义的很大,这种数据包,通常被称为socket攻击,字节流式攻击,导致服务器OOM。
由于:FrameDecoder里面的cumulation其实就是一个缓存的buffer对象 ,导致OOM
2. 防止Socket攻击
通过设置字节流Max大小,防止Socket攻击。
分析 Decoder 假设 Bytes_Max = 2048
a. 字节流大于Max,跳过所有字节流 // 防止Socket攻击
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
b. 当跳过所有字节流后,必须协议头 Protocol_Header 用于定位到下一个正确的协议开始的地方, 所有标准协议必须包括 协议header。 如果仅仅使用 定义一个稳定的结构 length + 数据,一旦Socket攻击,就无法正确解析后续协议包。 按照一个int,检测包头,通过跳过一个byte一个byte,不是int
buffer.markReaderIndex();
// 直到找到包头 如果Socket 攻击,4个字节,可能跳过包头
if (buffer.readInt() == SystemConstants.Protocol_Header_Flag) {
break;
}
buffer.resetReaderIndex();
buffer.readByte(); // 跳过一个Byte,开始比较下一组int是否等于Flag
//可读长度小于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
return null;
}
}
RequestDecoder public class RequestDecoder extends FrameDecoder {
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
// 可读长度必须大于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
// 数据包不完成,继续等待数据包
return null;
}
// 防止Socket攻击
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
// 记录包头开始位置
while (true) {
buffer.markReaderIndex();
// 直到找到包头 如果Socket 攻击,4个字节,可能跳过包头
if (buffer.readInt() == SystemConstants.Protocol_Header_Flag) {
break;
}
buffer.resetReaderIndex();
buffer.readByte(); // 跳过一个Byte,开始比较下一组int是否等于Flag
//可读长度小于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
return null;
}
}
int beginReader = buffer.readerIndex();
long requsetId = buffer.readLong();
short module = buffer.readShort();
short cmd = buffer.readShort();
int length = buffer.readInt();
// 判断请求数据是否到齐
if (buffer.readableBytes() < length) {
// reset 已经读取Base_Protocol_Length12字节
buffer.readerIndex(beginReader);
return null;
}
// 读取数据
byte[] data = new byte[length];
buffer.readBytes(data);
Request req = new Request();
req.setModule(module);
req.setCmd(cmd);
req.setData(data);
req.setRequestId(requsetId);
return req;
}
/**
* 数据包最小长度
*/
private static int Base_Protocol_Length = 4 + 8 + 2 + 2 + 4;
}
把长度定义的很大,这种数据包,通常被称为socket攻击,字节流式攻击,导致服务器OOM。
由于:FrameDecoder里面的cumulation其实就是一个缓存的buffer对象 ,导致OOM
2. 防止Socket攻击
通过设置字节流Max大小,防止Socket攻击。
分析 Decoder 假设 Bytes_Max = 2048
a. 字节流大于Max,跳过所有字节流 // 防止Socket攻击
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
b. 当跳过所有字节流后,必须协议头 Protocol_Header 用于定位到下一个正确的协议开始的地方, 所有标准协议必须包括 协议header。 如果仅仅使用 定义一个稳定的结构 length + 数据,一旦Socket攻击,就无法正确解析后续协议包。 按照一个int,检测包头,通过跳过一个byte一个byte,不是int
while (true) { buffer.markReaderIndex(); // 直到找到包头 如果Socket 攻击,4个字节,可能跳过包头 if (buffer.readInt() == SystemConstants.Protocol_Header_Flag) { break; } buffer.resetReaderIndex(); buffer.readByte(); // 跳过一个Byte,开始比较下一组int是否等于Flag //可读长度小于基本长度 if (buffer.readableBytes() < Base_Protocol_Length) { return null; } }c. 剩余字节流小于最小长度,放回null,继续等待 while (true) {
buffer.markReaderIndex();
// 直到找到包头 如果Socket 攻击,4个字节,可能跳过包头
if (buffer.readInt() == SystemConstants.Protocol_Header_Flag) {
break;
}
buffer.resetReaderIndex();
buffer.readByte(); // 跳过一个Byte,开始比较下一组int是否等于Flag
//可读长度小于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
return null;
}
}
RequestDecoder public class RequestDecoder extends FrameDecoder {
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
// 可读长度必须大于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
// 数据包不完成,继续等待数据包
return null;
}
// 防止Socket攻击
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
// 记录包头开始位置
while (true) {
buffer.markReaderIndex();
// 直到找到包头 如果Socket 攻击,4个字节,可能跳过包头
if (buffer.readInt() == SystemConstants.Protocol_Header_Flag) {
break;
}
buffer.resetReaderIndex();
buffer.readByte(); // 跳过一个Byte,开始比较下一组int是否等于Flag
//可读长度小于基本长度
if (buffer.readableBytes() < Base_Protocol_Length) {
return null;
}
}
int beginReader = buffer.readerIndex();
long requsetId = buffer.readLong();
short module = buffer.readShort();
short cmd = buffer.readShort();
int length = buffer.readInt();
// 判断请求数据是否到齐
if (buffer.readableBytes() < length) {
// reset 已经读取Base_Protocol_Length12字节
buffer.readerIndex(beginReader);
return null;
}
// 读取数据
byte[] data = new byte[length];
buffer.readBytes(data);
Request req = new Request();
req.setModule(module);
req.setCmd(cmd);
req.setData(data);
req.setRequestId(requsetId);
return req;
}
/**
* 数据包最小长度
*/
private static int Base_Protocol_Length = 4 + 8 + 2 + 2 + 4;
}
相关文章推荐
- Netty 快速入门系列 - Chapter 8 数据包协议【第二十讲】解决方案-Pipeline用例代码
- Netty 快速入门系列 - Chapter 7 数据包协议【第十六讲】数据传输问题
- Netty 快速入门系列 - Chapter 7 数据包协议【第十八讲】解决方案-经典协议包结构
- Netty 快速入门系列 - Chapter 7 数据包协议【第十九讲】解决方案-粘包分包原理
- Netty 快速入门系列 - Chapter 7 数据包协议【第十七讲】解决方案- 分割符 及 长度+数据
- Netty 快速入门系列 - Chapter 8 数据包协议【第二十一讲】FrameDecoder 讲解
- Netty 快速入门系列 - Chapter 6 自定义数据协议【第十三讲】 通过大端序列方法将4个字节int转成 byte数组
- Netty 快速入门系列 - Chapter 6 自定义数据协议【第十四讲】ByteBuffer 和 ChannelBuffers
- Netty 快速入门系列 - Chapter 6 自定义数据协议【第十五讲】自定义方法Serializer
- Netty 快速入门系列 - Chapter 1 传统OIO与NIO - NIO 【第二讲】
- Netty 快速入门系列 - Chapter 3 Netty5.x【第六讲】 -Netty5 案例
- Netty 快速入门系列 - Chapter 3 Netty5.x【第九讲】 - 单客户多Client 重连
- Netty 快速入门系列 - Chapter 4 Netty心跳【第十讲】 - IdleStateHandler学习
- Netty 快速入门系列 - Chapter 5 Netty之序列化【第十二讲】 Java Serializable
- Netty 快速入门系列 - Chapter 3 Netty5.x【第七讲】 - Channel线程安全?
- Netty 快速入门系列 - Chapter 5 Netty之序列化【第十一讲】 Protocol buff
- Netty 快速入门系列 - Chapter 1 传统OIO与NIO - 传统OIO 【第一讲】
- Netty 快速入门系列 - Chapter 2 Netty3.x 【第四讲】 - 基本原理
- Netty 快速入门系列 - Chapter 2 Netty3.x 【第五讲】 - 源码讲解
- Netty 快速入门系列 - Chapter 3 Netty5.x【第八讲】 - Client 重连