It is not unusual for Java code to deserialize data that comes from an untrusted source. A serializable class can overload the method readObject()
, which is called when an object of that class is being deserialized. This method (as well as similar methods such as readResolve
and readObjectNoData
) should treat the serialized data as potentially malicious. These methods should not perform dangerous operations, nor should they set the stage for such operations to be performed later in the deserialization process. For example, simply deserializing data should not invoke Runtime.exec()
.
If it is convenient for a serializable class to perform operations with potentially dangerous side effects during deserialization, it should require that the programmer expressly whitelist
Non-Compliant Code Example
The class OpenedFile
in the below noncompliant example opens a file during deserialization. Operating systems typically impose a limit on the number of open file handles per process, and this limit is typically is not very large (e.g., 1024). Consequently, deserializing a list of OpenedFile
objects can exhaust the process's available file handles and cause the program to malfunction.
import java.io.*; class OpenedFile implements Serializable { public String filename; public BufferedReader reader; public OpenedFile(String _filename) { filename = _filename; init(); } private void init() { try { reader = new BufferedReader(new FileReader(filename)); } catch (FileNotFoundException e) { } } private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(filename); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { filename = in.readUTF(); init(); } }
Compliant Solution
In the below compliant solution, the readObject
method throws an exception unless the potentially dangerous class is whitelisted.
import java.io.*; interface Whitelist { public boolean has(String className); } class WhitelistedObjectInputStream extends ObjectInputStream { Whitelist whitelist; public WhitelistedObjectInputStream(InputStream inputStream) throws IOException { super(inputStream); } public void setWhitelist(Whitelist wl) { whitelist = wl; } } class OpenedFile implements Serializable { public String filename; public BufferedReader reader; public OpenedFile(String _filename) { filename = _filename; init(); } private void init() { try { reader = new BufferedReader(new FileReader(filename)); } catch (FileNotFoundException e) { } } private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(filename); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { if (!(in instanceof WhitelistedObjectInputStream) || !((WhitelistedObjectInputStream) in).whitelist.has(this.getClass().getName())) { throw new SecurityException("Attempted to deserialize unexpected class."); } filename = in.readUTF(); init(); } }
Risk Assessment
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SER13-J | High | Likely | High | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Useful for developing exploits that detect violation of this rule |
Related Guidelines
Bibliography
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2894
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3253