It is often expected of a DevOps team to also take security into consideration when delivering software. Often however, this does not get the attention it deserves. In this blog post I’ll describe some easy to use, CI/CD pipeline friendly, open source tools you can use to perform several checks during your Java software delivery process which will help you identify and fix issues with minimal effort.
You can view my sample project here which implements all these tools. There is also a docker-compose.yml file supplied. SonarQube en Jenkins however do not come preconfigured in this setup. You can look at the Jenkinsfile (pipeline definition) to see what Jenkins configuration is required (installing plugins and creating credentials).
This is provided as an overview and small example only. It is not meant as a walkthrough to setup a CI/CD environment or a manual how to use the different tools. Also the mentioned tools are not all the tools which are available but a set of tools which are freely available, popular and which I managed to get working without too much effort. Using them together allows you to improve several aspects of the security of your Java applications during your software delivery process and provide quick feedback to developers. This can also help increase security awareness. When you have some experience with these tools you can implement more strict policies (which can let a build fail) and quality gates.
Static code analysis
What does it help you solve?
Static application security testing (SAST) helps you identify several issues in your source code by looking at the code itself before compilation. Some issues which static analysis can help prevent are;
- Exposed resources
Static analysis tools can help detect exposed resources. These might be vulnerable to attacks. - Hardcoded keys or credentials
If keys or passwords are hardcoded inside your application, attackers might be able to extract them by for example decompiling the Java code - Stability issues
This can be related to memory leaks such as not closing resources which will no longer be used. This can also be related to performance. Bad performance can lead to stability issues. An attacker might purposefully try to bring your application down by abusing performance issues.
Which tools can you use?
When talking about Java applications, the following tools are free to use to perform static analysis.
- PMD (here)
PMD can be used as a Maven or Gradle plugin (and probably also in other ways). It has a Jenkins plug-in available and the SonarQube Maven plugin can be supplied with parameters to send PMD reports to SonarQube. PMD can find a small number of actual security vulnerabilities. It can provide quite a lot of performance related suggestions. - SpotBugs (here)
SpotBugs is the spiritual successor of FindBugs. It can (like PMD) easily be integrated in builds and CI/CD pipelines. The SonarQube Maven plugin also supports sending PMD reports to SonarQube without additional plugin requirements. SpotBugs can find bugs in the area of security and performance among others (here). - FindSecBugs (here)
This is an extension of SpotBugs and can be used similarly. It covers the OWASP TOP 10 and detects /classifies several security related programming bugs.
In this example I did not use a Dockerfile but Dockerfiles can also not conform to best practices making them vulnerable. If you want to also check Dockerfiles, you can take a look at hadolint.
Dynamic Application Security Testing (DAST)
Some things cannot easily be automatically identified from within the code. For example, how the application looks on the outside. For this it is not unusual to perform a penetration test. Penetration tests can luckily be automated!
What does it help you solve?
- Scan web applications and analyse used headers
- Find exposed endpoints and security features of those endpoints
- Check OpenAPI / SOAP / Websocket communication/services
- Check traffic between browser and service
Which tools can you use?
- OWASP ZAP (Zed Attack Proxy)
Can run in a standalone container, started from a Maven build using a plugin, configured as a tool within Jenkins. You can install a plugin (here) in SonarQube to visualize results and use the HTML publisher plugin in Jenkins to show the results there. OWASP ZAP can also be used together with Selenium to passively scan traffic between the browser and the service or actively modify requests.
Dependency analyses
What does it help you solve?
- Java programs use different libraries to build and run. You can think of frameworks, drivers, utility code and many other re-usable assets. Those dependencies can contain vulnerabilities. Usually for older libraries, more vulnerabilities are known. Knowing about vulnerabilities in specific versions of libraries and their severity, can help you in prioritizing updating them.
Which tools can you use?
- OWASP Dependency Check (here)
Can run from a Maven build and supply data to Jenkins and SonarQube (by installing the Dependency-Check plugin). Also records CVSS scores (Common Vulnerability Scoring System) for risk assessments.
Container image vulnerability scanning
What does it help you solve?
- A Java program can run in a container on a container platform. A container is usually created by using a base image and putting an application inside. The base image contains operating system libraries or other software (such as a JVM) which can contain vulnerabilities.
Which tools can you use?
- Anchore Engine (here).
Anchore Engine is available as a container image. Once started, it will download information from various vulnerability databases. You can request a certain image to be analyzed. It has good integration with Jenkins, however not with SonarQube. Read my blog posts about using Anchore Engine here and here. You can get notifications when new vulnerabilities are discovered for already scanned containers which can be quite useful.
Putting it all together
In the below screenshot you can see the results from a sample Spring Boot REST service which you can find here. You can see results from SpotBugs/find-sec-bugs, PMD, OWASP ZAP and the OWASP Dependency-Check in SonarQube grouped together in a single view.
You can also find the different reports (PMD, OWASP ZAP, OWASP Dependency-Check, SpotBugs/find-sec-bugs) and also the Anchore Engine scan in Jenkins.
And you can open them to view details, for example from the Dependency-Check, SpotBugs and Anchore Engine below.
I recommend defining the plugins in a parent project and let every project inherit their configuration. This allows you to easily change it for many projects at once (when they upgrade to the latest version of the parent project). Letting the build fail when a certain level of vulnerabilities are discovered is also a good thing to do. Do not set this too strict or it will delay your development efforts significantly!
Quality gates can be defined centrally in SonarQube. See for example her. For Anchore Engine this is not easily possible however, since it lacks SonarQube integration. Instead it provides its own policy engine (and GUI in the commercial version to configure those policies). See for example here.
Finally
By using the above listed tools together for a Java application, you can quite easily report on issues in various areas related to security. Also they provide options for letting a build fail if certain rules are not kept. This will force people to fix security issues before going to production instead of having to play the blame-game when a security incident occurs. If you want to accept certain vulnerabilities, policies can also be configured for the various tools and on a higher level, Quality Gates can be defined.
These kind of automated tests take away a lot of manual work and help increase security awareness. They are however not as smart as to detect every issue. They focus on technical security aspects and not so much on functional ones. Peer reviews and for example privacy acceptance testing can help cover those.
These tools can also report too much and cause information overload. Try for example running them for the first time against a large application. It might take significant effort to evaluate, prioritize and solve every issue. Probably you should focus first on the most important ones like the ones reported as blockers or high severity. After that you can also prevent new high severity issues from arising and slowly work on the technical debt which is left.
There is always a balance between focus on non-functionals like security and functionals like business logic. Security is a topic to easily get lost in and it is important to always try and think about the actual risk involved which is specific to your application, your customer and the data involved. Does the investment for solving the issue outweigh the risk involved and possible damages? If the balance tips to functionals, you might be in danger, often due to poor life-cycle management, thus it is important to explain in business terms why certain things should deserve attention. This can be a challenge since the issues are usually described in terms which can be difficult to understand for business people. If the balance tips to non-functionals, you might get lost in detail trying to solve things which are only dangerous in theory but in practice can be ignored.