Vulnerability Management in Software Best Practices
In today’s interconnected world, vulnerabilities can appear when you least expect them, critically impacting your organization’s success. Employing effective vulnerability management, especially for software, begins with embedding security into the software development lifecycle (SDLC). By integrating security early in the software design and build process, vulnerabilities can be mitigated and managed from the first line of code written, all the way through to final deployment. This proactive approach ensures not only that vulnerabilities are caught earlier when they are cheaper and easier to fix but also that teams build their software with security in mind.
The best practices below provide any security engineer with the knowledge they need to create an effective vulnerability management plan for software and continue to remediate it once it is deployed.
Summary of vulnerability management in software best practices
Concept | Description |
---|---|
Manage your First Party code base | Every organization should ensure that its first-party code base is included in its vulnerability management program. |
Maintain an accurate software inventory | Effective vulnerability management in software requires understanding what software is being used by the organization. An accurate inventory can help organizations track dependencies, versions, and relevant stakeholders. |
Integrate security checks in the coding and build phases | Use automated security testing within the development environment and enforce secure coding practices. Make sure this flows through into your continuous integration and continuous delivery (CI/CD) pipelines. |
Test before release and deployment | Perform static and dynamic security scans in development/staging environments. Implement automated vulnerability checks within the pipeline to prevent vulnerable code from being released. |
Remediate vulnerabilities via continuous monitoring loops | Security progress is a constant process. By tracking changes, new vulnerabilities, production issues, and tying in relevant stakeholders, organizations can continue bringing insights into the SDLC to release more secure software. |
Manage your First Party code base
All software vulnerabilities stem from some kind of flaw in the software development process. Sometimes these flaws can be due to insecure coding methods, such as the incorrect allocation of memory to code variables. In other instances, they can occur due to the malicious use of intended functionality, such as manipulating the inputs or outputs of a piece of software to achieve unintended (and often harmful) results.
While it’s impossible to identify every software vulnerability in every application, effective vulnerability management in software development begins by defining the areas of software-specific vulnerabilities that an organization wants to address. From there, strategies can be implemented to reduce and mitigate the occurrence of these vulnerabilities from the outset.
This article focuses on two areas that generate software vulnerabilities: written code and third-party libraries. Both of these areas are within an organization’s ability to influence, with errors caused in the design or implementation of an organization-specific software solution. Other categories of software vulnerabilities, such as system-level vulnerabilities or third-party software vulnerabilities, are not considered in this article, as there would be a different process to manage them.
Written code
To understand the importance of written code, consider the code below. It shows a relatively simple software-specific vulnerability, written in a way that would enable an SQL injection attack to occur.
conn = sqlite3.connect('users.db') cursor = conn.cursor() username = input("Enter your username: ") password = input("Enter your userpassword: ") sql_query = 'SELECT * FROM Admins WHERE Name ="' + username + '" AND Pass ="' + password + '"' cursor.execute(sql_query) result = cursor.fetchone() if result: print("Access granted!") else: print("Access denied.")
Although this is a trivial example, it illustrates an important point for all SDLC considerations: The first place you should check for software vulnerabilities is in your own code.
Your developers have an incredible amount of power within your organization. In many cases, they are literally building the technical infrastructure that your entire organization rests upon. When they get it wrong, for whatever reason, it can have catastrophic consequences for your organization.
Take, for instance, the example above. The ten lines of code expose a critical vulnerability as they enable any person to insert arbitrary SQL code into the query. If, for instance, a person entered the inputs below, they would create an SQL statement that would always return True, effectively bypassing the authentication statement.
username = '" OR "1"="1' password = '" OR "1"="1' # Resulting SQL String: SELECT * FROM Admins WHERE Name ="" OR "1"="1" AND Pass ="" OR "1"="1"
As a result, those ten lines of code would allow an attacker to completely bypass your authentication mechanism and get the first user in the database. Given that the first user in a SQL user database is almost always the super admin, this could very quickly turn catastrophic. \
Be sure to manage your First Party code by integrating secure coding standards, effective code reviews, and automated code scans, all of which are discussed later in the article.
Here’s how the code above should be written. You can see here how much more effective it is to fix the code at the time of creation rather than responding to the vulnerability in the future:
conn = sqlite3.connect('users.db') cursor = conn.cursor() username = input("Enter your username: ") password = input("Enter your userpassword: ") cursor.execute("SELECT * FROM Admins WHERE Name = %s AND Pass= %s", (username, password))
Third-party libraries
It is rare to find anyone who writes code 100% from scratch. Doing so is time-consuming, costly, and in most cases results in a suboptimal outcome compared to code that has been custom-designed and built to solve the problem you need it for. These dependencies, called third-party libraries, are closely linked with the software ecosystem and allow applications to reuse code and ship more quickly.
However, it is not all sunshine and rainbows. Just like any third-party dependency in an organization, using third-party libraries has its own set of risks. While you may achieve gains in terms of code efficiency and consistency, you are also inheriting whatever risks the library brings. For instance, if that library has an SQL injection vulnerability in a function you are using…you now have it as well.
For instance, consider the Log4Shell vulnerability. Contained in the Log4j Java logging framework, this zero-day vulnerability gave attackers the ability to execute arbitrary Java code on up to 93% of enterprise cloud environments. The kicker is that this was a standard, well-used, open-source library, performing the important-but-boring task of helping software developers log what they were doing. Prior to this disclosure, it was almost laughable to consider that a library like this would rank as one of the most severe vulnerabilities ever.
Therefore, when considering your First Party code, carefully examine all the libraries it depends on. A great starting point for this is making sure you maintain an accurate inventory of all the third-party libraries you are using in your environment, as discussed in the next section.
Maintain an accurate software inventory
An accurate inventory of your organization’s software is essential for effective vulnerability management. When taken effectively, an inventory provides a comprehensive view of your tech stack’s versions, libraries, and interdependencies. It lets you see exactly what software (including third-party libraries) runs on which endpoint. Without knowing what software is being utilized, it becomes difficult to identify which software has vulnerabilities and which ones need to be remediated.
Imagine that the Log4Shell vulnerability has just come up, and you are the person tasked with remediation. The pressure is on—the exploit for the vulnerability is fairly simple, and everyone is panicking. On the surface, it seems simple to figure out. All you would need to know is:
- Where Java is being run in your organization
- Whether it has logging
- Whether it uses Log4j
However, imagine how hard this would be without an accurate software inventory. You would need to figure out who runs Java in your environment and ask them to audit every software repository for vulnerable versions. A task that could have taken minutes now takes hours or even days.
There are two aspects to an effective software inventory, discussed in the subsections below.
First Party code
The table below summarizes how a software inventory should work for First Party code.
Inventory information | Description | Example |
---|---|---|
All deployed software | Any software that is running in a production or UAT environment within the organization. This includes first-party code, third-party libraries, commercial off-the-shelf (COTS) software, and any other software used for management and control. | Web app backends, authentication APIs, etc. |
Versioning and configuration details | Details on software versions and specific configuration used in deployment. | Tracking that React is on v19.1.0 |
Interdependencies between different software | The relationships and dependencies between various components and services. | One package bundling another: @next15.3.3 -> @react19.1.0 |
General environment information
Software engineers should also be aware of the general environment in which their code base operates. This includes knowledge of the underlying operating system (including Dockerized versions) and applications in your environment. Understanding this will help you create more effective software and aid in vulnerability management.
For instance, imagine that a vulnerability in a popular Python library requires Windows Management Instrumentation (WMI) to execute. Knowing that your entire environment runs on Unix-based systems, such as Ubuntu and macOS, would remove almost all concerns in this area.
Start by creating a software bill of materials (SBOM) that contains a register of all your deployed software across the entirety of your operational environment, making vulnerability management much simpler. An SBOM can be maintained and created in multiple ways. For example, Device42 can track prohibited, purchased, and installed software and customize notification alerts when the inventory is updated.
Example of a SBOM from Device42 (source)
Integrate security checks
Effective SDLC includes manual and automated checks on every single piece of code that gets deployed. In a well-managed vulnerability management program, there should never be a scenario where code can make it to production without going through these checks.
Follow the steps in each of the sections discussed below.
Implement secure coding standards
Start by requiring adherence to secure coding standards. These standards have been developed over many years to help organizations eliminate software vulnerabilities during software development. They can be introduced through developer onboarding and enforced in automated code scans further down your continuous integration / continuous deployment (CI/CD) pipeline. Most modern integrated development environments (IDEs) can enforce secure software development practices before the software is even pushed to GitHub.
Here are some examples of secure standards you can implement in your organization:
- OWASP secure coding practices
- CERT secure coding standards
- MITRE CWE Top 25 + mapping to code
- CIS controls (v8)
Ensure that proper code reviews are conducted
A code review is a structured, manual process that helps identify bugs and improve code quality. It allows for a fresh set of eyes to be applied to a set of code, unbiased by the developer who initially wrote the code. In many ways, it is equivalent to a peer review process as used in other industries.
Follow these steps to implement a code review process into your SDLC:
- Set up a structured documentation process: Create a set of structured documentation processes that can be used for the review. This could be through GitHub or any other system that is easy to access and simple to use.
- Enforce two-person reviews in your code merging process: Ensure that no code can be merged into your main branch (or equivalent) unless it has had a second person review it.
- Define code review roles: Decide who will be responsible for performing code reviews. For most organizations, this will be senior software developers.
Automate code scans in the pipeline
Finally, leverage automation to integrate automated code scans in your deployment pipeline. Automated code scans ensure that every piece of code added to your application has been tested to the agreed-upon standard. While this won’t catch every vulnerability, it will mean that there is a baseline standard that eliminates well-known classes of vulnerabilities.
Many tools exist to integrate security scans into the deployment pipeline. Most organizations that want to ship code to users utilize a repository like GitHub or GitLab. These repositories allow for the configuration of pipelines that can run configurable checks on software before it is released, or even when it is pushed up to a branch or awaiting a merge as a pull request. Take advantage of the automated scans available to do the heavy lifting of finding vulnerabilities before they are exposed.
Some examples of tools you can use for this are listed below:
Tool | Description |
---|---|
SonarQube | An open-source platform performs static analysis of code to detect code smells and bugs, with integration in 29 different languages |
Semgrep | A static analysis tool that can enforce coding standards and security guardrails |
CodeQL | A code analysis engine developed by GitHub to automate security checks |
Test before release and deployment
Effective vulnerability management in software relies heavily on well-configured CI/CD pipelines. These pipelines enable organizations to pass all software through a series of checks before merging it into a production environment. When applications don’t meet specific requirements, they can be blocked before ever causing issues.
There are three best practices to implement in this area.
Perform code and container scans
Good software vulnerability management combines code scans and container scans (if applications are being deployed as a containerized solution). Doing one scan does not mean you can omit the other—they work in tandem to ensure that the whole deployment process is covered.
Code scans help to analyze your source code to detect vulnerabilities. Modern code scanners go beyond just analyzing your hand-written code, also including all of your third-party libraries. For instance, Snyk Code, when integrated into your CI/CD pipeline, can block a software release if one of the included libraries has a critical bug.
In contrast, container scans look at the base operating system and installed packages in the image. Like code scans, these scans detect known vulnerabilities in the underlying image, ensuring that the software operates on a known secure baseline.
Here is a table showing the differences between container and code scans.
Code scans | Container scans | |
---|---|---|
What it scans | Mainly source code that is in the repository | The container image and base OS layers |
When it runs | Early in the pipeline, during a push or merge request, depending on the setup | After the container is built |
Common tools | Sonarqube, Semgrep, CodeQL | Clair, Trivy |
Target risk | Insecure libraries, known harmful code smells, vulnerable code logic | Vulnerable packages in the base image |
Blocking criteria | Critical code flaws or code patterns that fail secure coding standards set by the engineer | Critical vulnerabilities found in the base image |
Include static application security testing (SAST)
SAST helps identify insecure or less secure areas of the code. It analyzes a code base to identify actual vulnerabilities or areas of the code that are likely to lead to vulnerabilities. Although SAST is typically performed well before the CI/CD pipeline is engaged, it should still be included in your CI/CD pipeline to ensure that no code sneaks in. For instance, it can identify when SQL statements are coded incorrectly (such as in the first section) and point developers to where the issue occurs.
Run dynamic application security testing (DAST)
DAST helps organizations identify vulnerabilities in their compiled code. It simulates actions that demonstrate vulnerabilities, assisting organizations in identifying vulnerabilities before an adversary does. For instance, it might simulate an SQL injection attack on an application input.
Remediate vulnerabilities via continuous monitoring loops
Security progress is a continuous process. By tracking changes, new vulnerabilities, production issues, and tying in relevant stakeholders, organizations can continue bringing insights into the SDLC to release more secure software. When vulnerabilities are found, they should be remediated in the earlier stages (staging/development) so the team can better understand the impact on production before rolling to prod. So, how does one remediate vulnerabilities in each stage of the SDLC and ensure that monitoring occurs?
Just as it is crucial to maintain an accurate software inventory, ensuring that this inventory can be monitored and alerted when changes occur is vital for getting feedback on any vulnerabilities that would be found. When versions update and vulnerabilities are revealed, knowing which software is running which version allows engineers to patch or rollback to a safe and stable version. This is one reason a software inventory and an SBOM are critical in software vulnerability management. This continuous monitoring of updating inventory allows teams to know which versions are behind and which ones are at a stable version.
The workflow below shows an example for remediating vulnerabilities within a continuous feedback loop.
Continuous monitoring process (source)
At each stage, the tools and processes used can be implemented throughout the SDLC:
- Continuous monitoring system: Tools such as Sonarqube, Trivy, and Clair monitor source code, dependencies, and containers as applications are built and pushed through the pipeline. Even processes like code review can be considered continuous monitoring systems, as code is checked before it is pushed.
- Detect vulnerabilities: Detections from scans show up in reports, allowing security engineers to respond appropriately.
- Log and triage: Collect all information relevant to the found vulnerability and assign it to the parties responsible for handling the resource.
- Assess risk: Evaluate the impact of the vulnerability on the organization
- Plan mitigation: Map the mitigation responsibilities to the affected assets. See if systems need to be isolated, updated in staging, etc, before going into production.
- Remediate: Patch or fix the bug found.
- Test and verify: Rerun scans or code checks to make sure that the vulnerability is gone.
- Close or escalate: Close the ticket or escalate if the vulnerability is unable to be remediated.
- Update audit trail: Update logs, inventory, and compliance records.
Vulnerability management is a continuous process in software. Ensuring a feedback loop throughout the SDLC will minimize the vulnerabilities that adversaries can exploit in your production systems.
Conclusion
Vulnerability best practices for software involve multiple proactive steps within the SDLC to help mitigate threats against an organization. They ensure that security is baked in and considered through every stage. Patching vulnerabilities in software is often not enough. Instead, vulnerability management is done before code is written and tracked. Once software is deployed, vulnerability management is a continuous process of monitoring its security as technologies evolve and new vulnerabilities are found.
A proactive plan and an effective DevSecOps process are the standards of good software management. Doing so ensures that you continually reduce risk to your organization instead of reacting to threats when they happen.