【发布时间】:2014-06-16 19:11:20
【问题描述】:
我目前正在编写一个客户端/服务器应用程序,使用 Google 协议缓冲区对底层消息进行编码。由于它是一个 .NET 程序,我使用的是 protobuf-net 库:它快速、便携且经过深思熟虑。
但是,我有点担心潜在的 DoS 攻击。服务器应用程序将直接面向互联网,并且底层协议将被公开记录。攻击者可以很容易地制作一个数据包来耗尽内存。
一个简单的例子:
给定以下原型定义:
[ProtoContract]
class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
}
以下代码将尝试为Bar 字符串分配2GB 缓冲区:
byte[] bytes = { 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0x07 };
Serializer.Deserialize<Foo>(new MemoryStream(bytes));
立即生成OutOfMemoryException。当然,在这里它并不重要:运行时将无法分配它(至少在 32 位进程中)。现在制作多个数据包,每个数据包分配数百 MB 内存,并观察正常操作因内存不足而失败。当然,如果攻击者在字符串长度之后不发送任何内容,GC 会迅速回收内存,但增加内存压力还是太容易了。
我知道这个问题并不是 protobuf-net 所特有的:任何使用长度前缀数据的协议都可能会遇到同样的问题。通常,它们是使用任意配额来解决的。但是,我没有找到任何方法在 protobuf-net 中指定它们。例如,我想指定一个字符串不能超过 N 个字符,一个给定的列表不能有更多的 M 个元素等等。
是否有任何简单的解决方案来防止我错过的这个问题(而不是自己实现或分叉)?
【问题讨论】:
-
我不知道是否有更好的方法,AFAIK 如果你放弃 normal 解析并使用 reflection i>(至少作为早期验证):
reflection->FieldSize(foo, descriptor->FindFieldByName("Bar")) <= 1024)。 (抱歉,它是 C++ 语法,我从未使用过 .NET 版本,但我猜它非常相似)。 -
如果需要的话,我确信我们可以添加配额支持...简单地说:还没有。
-
@Adriano:我不确定 protobuf-net 是否支持此功能而无需解析。
-
@MarcGravell 谢谢。是计划好的吗?你接受拉取请求吗?
-
@JulienLebosquain 遗憾的是我从未使用过它,所以我不知道它是否支持一切(但我猜它可能是,反射是相当不错的部分)。
标签: .net security protobuf-net