[Java] add more defensive version of deserialize(byte, class)
Feature Request
I debugged the method deserialize(byte, class) and found that it does not check the deserialized data matches the class - that it just info for the compiler to know what the expected return type is. Would it be possible to add a deserializeWithClassCheck(byte, class) that validates the byte data has a class marker saved into the byte data and that class data relates to a class that is the class expected (the one passed in the parameter list of deserializeWithClassCheck) or a subclass?
Is your feature request related to a problem? Please describe
The idea is that users who have a secure network can avoid registering all the classes that they need on the Fory instance but have the deserializeWithClassCheck call validate the encoded data is not related to some unexpected class. deserialize(byte, class) will deserialize and you will get a Class Cast Exception but it is good to have a method that avoids creating class instances when the encoded data is of wrong type.
Describe the solution you'd like
No response
Describe alternatives you've considered
No response
Additional context
No response
The compiler will do such check, if wrong type passed, a ClassCastException will be throwed. I prefer not adding such check in fory, the class check is slow, and adding new API introduce extra complexibility
So if you set requireClassRegistration(false) and a malicious user knows of a RCE gadget not disallowed via hardcode in Fory, then Fory will happily accept the RCE even if the user tells Fory what class they are expecting by calling deserialize(bytes, class). deserialize(bytes, class) doesn't care what value is passed in other than to cast the result. Most serde libs provide stronger guarantees than this.
Not exactly, even unsafe classes are passed when deserializeing, fory will still do security check.
For example:
UnsafeClass o = new UnsafeClass();
fory.serialize(o); // will fail
byte[] unsafeBytes = ...
fory.deserialize(bytes, UnsafeClass.class); // will fail
fory.deserializeJavaObject(bytes, UnsafeClass.class); // will fail
But following code can succeed:
UnsafeClass o = new UnsafeClass();
fory.registerSerializer(UnsafeClass.class, UnsafeClassSerializer.class)
fory.serialize(o);
byte[] unsafeBytes = ...
fory.deserialize(bytes, UnsafeClass.class);
fory.deserializeJavaObject(bytes, UnsafeClass.class);
@chaokunyang Fory has a bult-in disallow list. This is good but it not a full list of every dangerous class. I don't care about serialize but I care about deserialize. deserialize is the attack vector - malicious users can hack Fory to serialize dangerous bytes and send to an app that they will know will try to deserialize the bytes with Fory. They might know about a dangerous class that Fory team does not know about. But if Fory checks the class in the bytes it receives and validates that it matches what the user expects, then things are safer.
@pjfanning That check is what class registration do. It check whether class in the bytes are registered by users, if it is registered by users, then fory take it as safe.
@pjfanning We have another mode to allow users costomize such behaviour. One user can disable class registration, but use ClassChecker to control which classes are allowed to deserialization:
Fory fory = Fory.builder().requireClassRegistration(false).build();
fory.getClassResolver().setClassChecker(
(classResolver, className) -> className.startsWith("org.example."));