我为聊天程序创建了一个类消息,其中包含有关特定消息的所有信息。消息实例在服务器和客户端之间来回发送。我使用序列化(ObjectInputStream / ObjectOutputStream)来交换这些消息。
我无法控制序列化和反序列化的过程。对我来说最大的问题是如果一些攻击者想发送一个非常重的消息(比如说几千兆字节),我就没办法避免它,我可能会遇到一些严重的内存问题。这可以用于拒绝服务,这很严重。
我看了很多关于序列化及其安全问题的不同主题,但没有人提到如何避免这个微不足道的问题。我希望能够使用缓冲区将数据作为字节数组检索,以便我可以随时停止读取。但我无法通过序列化找到任何方法。 问题 :这是我应该如何处理的?
欢迎任何关于序列化安全问题的评论。我听说你可以在一个可序列化的类的实例中注入恶意代码,但是这怎么可能,因为在客户端和服务器上类的定义必须相同?
提前感谢您的帮助!
答案 0 :(得分:0)
为什么不写一个FilterInputStream(http://docs.oracle.com/javase/6/docs/api/java/io/FilterInputStream.html)来抛出异常,如果它从其底层流中读取超过X字节的数据?
您可以使用一种方法来替换此页面上对readObject的调用:
/**
* A method to replace the unsafe ObjectInputStream.readObject() method built into Java. This method
* checks to be sure the classes referenced are safe, the number of objects is limited to something sane,
* and the number of bytes is limited to a reasonable number. The returned Object is also cast to the
* specified type.
*
* @param type Class representing the object type expected to be returned
* @param safeClasses List of Classes allowed in serialized object being read
* @param maxObjects long representing the maximum number of objects allowed inside the serialized object being read
* @param maxBytes long representing the maximum number of bytes allowed to be read from the InputStream
* @param in InputStream containing an untrusted serialized object
* @return Object read from the stream (cast to the Class of the type parameter)
* @throws IOException
* @throws ClassNotFoundException
*/
@SuppressWarnings("unchecked")
public static T safeReadObject(Class<?> type, List<Class<?>> safeClasses, long maxObjects, long maxBytes, InputStream in ) throws IOException, ClassNotFoundException {
// create an input stream limited to a certain number of bytes
InputStream lis = new FilterInputStream( in ) {
private long len = 0;
public int read() throws IOException {
int val = super.read();
if (val != -1) {
len++;
checkLength();
}
return val;
}
public int read(byte[] b, int off, int len) throws IOException {
int val = super.read(b, off, len);
if (val > 0) {
len += val;
checkLength();
}
return val;
}
private void checkLength() throws IOException {
if (len > maxBytes) {
throw new SecurityException("Security violation: attempt to deserialize too many bytes from stream. Limit is " + maxBytes);
}
}
};
// create an object input stream that checks classes and limits the number of objects to read
ObjectInputStream ois = new ObjectInputStream( lis ) {
private int objCount = 0;
boolean b = enableResolveObject(true);
protected Object resolveObject(Object obj) throws IOException {
if ( objCount++ > maxObjects ) throw new SecurityException( "Security violation: attempt to deserialize too many objects from stream. Limit is " + maxObjects );
Object object = super.resolveObject(obj);
return object;
}
protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
Class<?> clazz = super.resolveClass(osc);
if (
clazz.isArray() ||
clazz.equals(type) ||
clazz.equals(String.class) ||
Number.class.isAssignableFrom(clazz) ||
safeClasses.contains(clazz)
) return clazz;
throw new SecurityException("Security violation: attempt to deserialize unauthorized " + clazz);
}
};
// use the protected ObjectInputStream to read object safely and cast to T
return (T)ois.readObject();
}