In the last article, we discussed various issues which can occur while using the Open Source Software (OSS). The problem while using OSS libraries is that we can’t verify the development steps (or standards, if any) followed by the OSS vendor/developer team. OSS libraries provided by well-known sources like Apache, Spring or Hibernate follow best practices before releasing the code but other vendors may or may not have such a practice. This security gap can create opportunities for attackers to exploit the code of an OSS library in a productive application. As per the report from GitHub, security vulnerabilities go undetected for more than 4 years before been disclosed and 59% of package ecosystems like Java etc. have a chance of getting security alerts in the next 12 months. Hence, it’s the responsibility of the consuming application/product to not only test and analyse the code written by their dev team but also the code getting imported from these libraries.
Most of the development teams use tools like Fortify, CodeQL, Eclipse Steady or CheckMarx for doing SAST (Static Application Security Testing). These tools are very powerful in finding possible caveats in the code but lack the analysis of OSS libraries because there are either runtime dependencies or container level dependencies that can’t get picked by these tools. Additionally, some development teams are more progressive in doing DAST (Dynamic Application Security Testing) using tools like Burp or OWASP ZAP. These tools provide support and extensibility to test any application on the grounds of well-known attacks like Replay attacks, JWT poising etc. But, these tools also cannot test the application end-to-end as it will take time and as these tools primarily run in test systems many container levels and OSS level dependencies either don’t get activated or not are covered as part of the active application data flow route of testing.
This leaves a narrow gap in overall software security which can lead to data breaches or compromise CIA triads. We analyzed one of the projects under development in my unit and found the development team has imported libraries which, if taken as source code would account for 86K LoC (Lines of Code), compared to 3K LoC written by the team. Additionally, the project imports 566 libraries – which also would need to be checked for security issues. Now, this is a very huge number. Most of the libraries are commonly used libraries but some are used for either solving a specific piece of the business problem or to help the developers in accomplishing a certain goal like data parsing, cryptography etc. Upon further analysis, we found approx. 50% of the libraries used by the development teams are not used anywhere in the code. For example, let’s take Spring Boot Actuator, one of the most commonly used frameworks in microservice development in JAVA. If we analyze the dependency tree of Spring Boot Actuator we will find libraries for logging support (Spring Boot Starter Logging). This logging support library has a dependency on other low-level logging frameworks like Logback, SLF4J etc. These libraries get added as part of the transitive dependencies via such frameworks. These libraries are sometimes required but most of the time are added as additional libs because of being part of the framework. Such transitive dependencies make the project BOM(Bill Of Material) fat. There are tools like DepClean (Java) or Mininode (NodeJS) that can help the team to reduce the BOM (Bill Of Material) for the development teams to add only those dependencies which are directly referenced in the code either via API call or required to run the application. But, these tools are not 100% accurate as these tools run using static code analysis which can remove certain libraries which get invoked dynamically during application runtime.
Most of the development teams don’t use all the APIs provided by the libraries. Hence, another mechanism that can be used by development teams is to reduce the attack surfaces exposed by these libraries by restricting the visibility of the APIs. This can be done in many ways, for example creating interfaces in Java for the OSS libraries and only expose the APIs required by the development team. By doing this, if the unused APIs possess any vulnerabilities, they can’t be exploited in the runtime. For the exposed APIs, however, development teams should perform thorough security validation. Another solution for this problem is to shrink the libraries to have only the required code in them. Tools like JShrink can help create shrink versions of the Java libraries.
In conclusion, no matter what is the source of the code i.e. self-written or OSS libraries, you should always follow strict mechanisms for security testing of the code. As any of the unknown vulnerabilities can become a threat vector for your application. So, during the product development, it’s recommended to adopt security testing and vulnerability analysis for the OSS libraries. Hence, before releasing the product/application we recommend:
- Perform a vulnerability analysis of the open-source used in the project for both known vulnerabilities and unknown vulnerabilities.
- Remove transitive dependencies which are either not required or referenced by your code
In the next article, we will share how the team can perform vulnerability analysis for the open-source library to uncover unknown vulnerabilities. Stay tuned.