Resources
Web Security

How to integrate continuous API fuzzing into the CI/CD?

No items found.
 - 
January 17, 2023

Integrating continuous fuzzing into the pipeline provides an automated alternative method to identify and fix vulnerabilities in SDLC before they become a problem.

You information will be kept Private
Table of Contents

API security is a growing concern for businesses that offer or consume APIs. APIs, or application programming interfaces, allow different software systems to communicate and exchange data. They allow businesses to build integrations and connect with partners, customers, and other stakeholders.

However, as more sensitive data is being shared through APIs, it is essential to ensure that these interfaces are secure and protected from unauthorized access or manipulation.

In this blog post, we’ll discuss how continuous fuzzing can be a powerful tool to secure APIs and how developers can adopt a “secure by default” approach by integrating continuous fuzzing into SDLC processes.

Fuzzing can be applied to any function but for this blog post, we will discover how we can fuzz REST API payloads using Golang’s fuzzing library.

What is Fuzzing?

Fuzzing, also known as fuzz testing, is a type of software testing that involves feeding invalid, unexpected, or random data to a program and observing how it responds. The goal of fuzz testing is to identify vulnerabilities in a program that attackers could potentially exploit.

Fuzzing is a powerful method for finding security vulnerabilities because it can simulate attacks that hackers might use to exploit a program. By sending a large number of different inputs to the program, fuzz testing can uncover vulnerabilities that other testing methods might not detect.

There are many ways to fuzz your code in a staging environment but we want the fuzzing process to become a part of our Software Development Life Cycle. To achieve this, we’ll use the Go programming language. In Go 1.8, fuzzing was introduced as a part of the standard testing library, and it’s straightforward to implement some fuzzer functions as a part of unit tests, and we can “fuzz” our code without any external tool or a library.

Note that you don’t need to have unit tests for fuzzing. Nevertheless, having unit tests will help us as a base for our fuzz test.

Let’s start with a simple fuzzing example which should look like the following snippet. First, we need to feed the fuzzer with some seed corpus (sample input), and the fuzzing library will call the target function Reverse() with some random input generated from the seed data. If the function fails at some point, we will catch it:

We need to keep in mind that fuzzing is an expensive operation, and it ends only if there is a crash. That’s the downside, so we should decide how frequently the function should be fuzzed according to the criticality. Many critical applications (like google-chrome) are being fuzzed constantly. In this example, we’ll fuzz frequently (in each build) but for a very short time.

Luckily, go tools support this option as well:

As the example above shows, fuzzing enables developers to test for the unexpected. It does not replace the need for other types of tests but rather complements them. It is a great way to increase test coverage and identify test cases.

From a security perspective, fuzzing continuously is essential for several reasons.

  • Identifying input validation vulnerabilities: APIs often rely on input validation to ensure that only valid data is accepted. Fuzz testing can help identify input validation vulnerabilities by sending many input values to the API and observing how it responds.
  • Testing for robustness: Fuzz testing can help developers determine whether their API is robust enough to handle various inputs, including invalid, unexpected, or malicious data. This can help ensure that the API is secure and can withstand attacks.
  • Uncovering hidden/logic vulnerabilities: Fuzz testing can help identify hidden/logic vulnerabilities in a program that might not be immediately apparent. By sending a large number of different inputs to the program, fuzz testing can uncover vulnerabilities that other testing methods might not detect.

Let’s use this approach to fuzz our REST endpoints and add fuzz tests into the DevOps pipeline to run it on every build.

The example API can be found at https://github.com/kondukto-io/simple-fuzzing. The project layout is simple as follows and self-explanatory:

For the sake of this blog post, we will focus on the /handlers directory but first, let’s investigate the server.go file.

The code is pretty straightforward, and to keep it even simpler, we have two handlers: CreateUser and GetUserByID.

The idea is to write a fuzz test for each endpoint, and to do that, we need to look at the handler function.

The handler does the following:

  1. There is a User model (struct) with three fields.
  2. The handler expects a JSON request and maps the data with the User model.
  3. The given User input is inserted into the USERS table in the database.
  4. If an error occurs, the function returns a HTTP status code 400 - BadRequest with the error message.

As you can see, there is no data validation in the handler, but the INSERT operation uses a parameterized query.

Ideally, we prefer to derive our fuzz tests from unit tests to maintain the structure as is. It is easier, and adding more test cases will increase the fuzzer’s seed corpus.

Finally, we run a fuzz test and wait for the crash. Go’s fuzzing library will store all the crash cases in the testdata directory. So, whenever a crash occurs, the fuzzer will test this parameter again.

As we discussed previously, fuzzing is a never-ending process, that’s why fuzzing only  "critical" endpoints can be a good option.

Finally, we can add these fuzz tests to our CI/CD pipeline to continuously fuzz our endpoints on each build or each PR.

The go tools do not support multiple fuzzing at the moment. We can fuzz each handler separately.

Conclusion

Testing is crucial to increase the quality of the software we develop and fuzzing is an effective and proven method to find bugs in software.

From a security engineering perspective, fuzz testing can be an effective way to achieve a “secure by default” approach in development.

In this blog post, we wanted to show you an alternative approach to improve the “security culture” among developers and how continuous fuzzing in the pipeline can be used as a security measure in (API) development.

Next time we will introduce some vulnerabilities in the API and hope to find them with fuzz testing.

Reach out to us if you have any questions about how to implement a DevSecOps pipeline from scratch or simply request a demo of the Invicti Platform to see how it works with your environment.

Resources:

Frequently asked questions

No items found.
Table of Contents