One of the most common uses of APIs is integrating libraries and frameworks that has a required behavior to a new software in order to cut down on the development time. It is indeed a very efficient and encouraged mode of developing new software, because
Therefore any security vulnerabilities exposed through these calls has to be mitigated by the application itself, thus the secure use of library APIs should be thoroughly emphasized during software development. The security concept of protecting oneself against its environment, no matter how fundamental the environment is to the software reemerges here, as observed in security of Virtual Machine Operating Systems (Refer – Overshadow). The idea further relates to the network designing concept of Zero-trust.
A recent example of a library related vulnerability is the Heartbleed bug (CVE-2014-0160) of the OpenSSL implementation’s TLS/DTLS (transport layer security protocols) heartbeat extension. The recipient of a heartbeat message responds with the same message, but with different random padding. More critically, recipient depends on the sender to specify correct payload lengths. So a malicious sender who modified payload length beyond his message would obtain a reply from the recipient who had read memory beyond the actual message, leaking secure data. Since the bug has been around for a while before public disclosure, the impact was severe. (Refer Attacks of SSL for more information.)
memcpy(bp, pl, payload);
Interpretation: Copy memory from “pl” location to “bp” location, up to the length “payload”.
/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0; /* silently discard */
...
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
A noteworthy point here would be that heartbeat extension was only required for encrypted UDP communication, and thus could have been disabled for many servers who didn’t require it. (Recompiled with an option to disable heartbeat protocol). So a significant recommendation of secure use of APIs would be practicing system hardening when using open source libraries. Another point to note would be that despite open source software is reviewed by many users, oversights are still possible and implicit trust should not be placed upon their security.
However, while vigilance can be exercised, the responsibility of securing a program against this type of vulnerability may not be a developer responsibility.
A more developer oriented example would be the Java Serialization Bug (CVE-2015-6420), which also had a massive impact. Serialization is a process used to transfer application native objects from one point to another, (e.g. between clients and servers) over a network as a byte stream. Application can deserialize the stream of data, and the object would be constructed, ready to use by the end point. However, serialized data received by Java applications were not validated upon deserialization. Thus if malicious, specially crafted objects were sent to a server, the server would execute it. Researchers demonstrated that major frameworks such as WebSphere, WebLogic, and JBoss were exploited through this bug, which were made vulnerable through inclusion of Apache Commons Collections library.
The fault exists in both parties, such that untrusted deserialization should not exist on seasoned libraries, or the subsequent applications or frameworks that use the library should not place trust it to implement their security for them.
New releases of Commons Collections library has disabled the insecure functionality, but due to commonality of serialization, other vulnerable libraries are likely to exist. Careful validation before object construction from the deserialized data (which is when arbitrary code execution happens) could protect against this vulnerability. CERT Oracle Coding Standard for Java guidelines SER12-J and SER13-J provides adequate instructions on how to properly overcome this vulnerability by developers. The code extracted from these instructions are included below.
SER12-J. Prevent deserialization of untrusted data guideline suggests a whitelisting approach.
class DeserializeExample {
public static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
Object ret = null;
try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
ret = ois.readObject();
}
}
return ret;
}
}
Here object is directly returned before any validation. Thus, any method that receives this output would use it “as is”, allowing malicious code execution.
Inspect the class of any object being deserialized, before its readObject() method is invoked. A set of whitelisted class names are given and equality of class name and serialVersionUID is considered before deserialization. This solution is called “Look-Ahead Java Deserialization”
class WhitelistedObjectInputStream extends ObjectInputStream {
public Set whitelist;
public WhitelistedObjectInputStream(InputStream inputStream, Set wl) throws IOException {
super(inputStream);
whitelist = wl;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass cls) throws IOException, ClassNotFoundException {
if (!whitelist.contains(cls.getName())) {
throw new InvalidClassException("Unexpected serialized class", cls.getName());
}
return super.resolveClass(cls);
}
}
class DeserializeExample {
private static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
Object ret = null;
Set whitelist = new HashSet<String>(Arrays.asList(new String[]{"GoodClass1","GoodClass2"}));
try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
try (WhitelistedObjectInputStream ois = new WhitelistedObjectInputStream(bais, whitelist)) {
ret = ois.readObject();
}
}
return ret;
}
}
SEC58-J. Deserialization methods should not perform potentially dangerous operations guideline suggests a restrictive approach.
class OpenedFile implements Serializable {
String filename;
BufferedReader reader;
public OpenedFile(String filename) throws FileNotFoundException {
this.filename = filename;
init();
}
private void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
filename = in.readUTF();
init();
}
}
The init method which reads the file is called in the constructor, thus the file is read during deserialization. Reading while receiving a number of such requests could cause consume all available resources.
Instantiate first. Initialize later if necessary.
class OpenedFile implements Serializable {
String filename;
BufferedReader reader;
boolean isInitialized;
public OpenedFile(String filename) {
this.filename = filename;
isInitialized = false;
}
public void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
isInitialized = true;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
filename = in.readUTF();
isInitialized = false;
}
}
If constructor contains dangerous operations, then the class must be restricted from being deserialized. Thus, readObject() method is overridden to throw an exception.
class Unchangeable implements Serializable {
// ...
}
class OpenedFile extends Unchangeable { // Serializable, unfortunately
String filename;
BufferedReader reader;
boolean isInitialized;
public OpenedFile(String filename) {
this.filename = filename;
isInitialized = false;
}
public void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
isInitialized = true;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new NotSerializableException(OpenedFile.getClass().getName());
}
}
In summary, the recommended practice would be to not have any illusions regarding the weaknesses external libraries present to the security posture of your application, and therein the threat landscape you have to navigate. In summary, following recommendations for securing your application when using external libraries can be suggested.
“Understand How Integrating External Components Changes Your Attack Surface.” IEEE Cybersecurity [Online]
“Add heartbeat extension bounds check.” Commit Diff by Dr. Stephen Henson. [Online]
“SEC58-J. Deserialization methods should not perform potentially dangerous operations” [Online]
“SER12-J. Prevent deserialization of untrusted data” [Online]
“What is the Heartbleed bug, how does it work and how was it fixed?”. Josh Fruhlinger [Online]
“Why The Java Deserialization Bug Is A Big Deal”. Jai Vijayan [Online]
“The 10 Worst Vulnerabilities of The Last 10 Years”. Jai Vijayan [Online]
“Apache Commons Collections Java library insecurely deserializes data” [Online]