Level Up Your Source Code Review Process

Abdullah Aloraini
24/03/2024


Introduction

In this blog, we will explore the basics of code review and how to start the assessment of an application's source code. Let's begin by understanding what we mean by code review. In simple terms, it's the process of examining code by someone who is not the author(s) of the application code. It's important that the reviewer is another person to ensure the authenticity of the process.

The purpose of this review can vary depending on why you need it. For example, it can be to find security flaws, identify performance issues, or refactor code. In this blog, we are solely interested in security code review.



Knowing the Basics

Since the market has many languages and each language has many frameworks and libraries, it's impossible to master each language and framework, but the good thing is that most programming languages share the same concepts. So, as a starter, you might focus your efforts on learning general programming concepts. Then shift your focus towards mastering one programming language. It's a good idea to focus on one of the languages that is used widely in the market, for example: C#, java, and php.



Let's get back to code review; we might divide the vulnerabilities found during the assessment into three categories:

1-General applications' vulnerabilities like input injection and insecure design.

2-Vulnerabilities related to misconfigurations of servers. For example, using insecure connections (HTTP).

3-Vulnerabilities related to specific languages, systems, or frameworks. A good example here is the buffer overflow in C and C++. Sometimes, you might want to focus on the version of the framework also because some updates change the default behavior of framework and this could cause security flaws if the developer is not aware.





Source Code Review Methodology

The source code review process goes through four phases:
Understand The application

At the first step of any code assessment, you have to read the documents related to that application. These documents, such as Software Requirements Specification (SRS), Process Flow, and Usage Documents, are crucial. Additionally, you should have a session with the developers to explain the application, its purpose, the flow, and how they implement sensitive mechanisms such as authentication, authorization, and validation, if possible.

Also, the most important thing here is to understand how the application's folders are structed. What does each folder mean and how are they used?

Now, you might want to take notes about possible threats. You might think of this as a threat modeling.

Let's take an example of the Contact Us form which is usually used in many applications.
Here, we notice different input fields, so we might think about injection issues such as SQL injection, XSS, and so on. Furthermore, we might consider that since there are multiple parameters passed to the application in the form, there might be hidden parameters that developers forget to remove that we can manipulate or change; this is called Mass assignment.



Additionally, we notice that this form adds values to the database. Therefore, we should consider protection against database flooding attacks.



So, now we have an overview of some possible risks in this function within the application:

a.Does the application validate users' inputs in the form before processing them?

b.Does the application use the users' inputs in other parts of the code as output with proper encoding?

c. Does the application apply protection against flooding requests, for example, CAPTCHA?

d.Etc.

Each of these questions, during the code review, will bring up new questions. For instance, let's consider if the application is implementing input validation—is it correct? Can it be bypassed? And so on.





Conduct Automated Scan

As the second step, we run automated scans on the source code. During this phase, we can perform the following actions:

Conduct an Automated scan of the source code.

Utilize commercial tools such as SonarQube, Fortify SCA, Bandit, GitHub, Checkmarx, etc. This process is known as Static Application Security Testing (SAST). Certain tools require the capability to build applications from the source code as they convert it into an intermediate format. For example, in Fortify SCA, the source code is transformed into Fortify Project Results (FPR) format before analysis.
Like any automated tool, false positives mostly will be found in the results which then we have to verify by looking into each issue with the source code. Some of the issues might require more effort to verify than others. For instance, hard-coded credential issues can be easily verified, as they don't require extensive checks. However, vulnerabilities related to injection or weak cryptography demand more thorough verification:
Dependencies Scanning or Software Composition Analysis (SCA)

Nowadays, most software relies on open-source dependencies to function. While this brings simplicity, it also may introduce security flaws associated with these dependencies. Therefore, as part of the code review, you should check these packages to ensure there are no vulnerabilities associated with the versions used within the application. Many open-source tools perform this depending on the technology used. The following list is an example of the tools used:

1.Snyk Open Source

2.Checkmarx SCA

3.OWASP dependency-check

4.Safety or dependency-check-py for pythons

5.Bundler-audit for ruby



Then based on the output, you can check on the vulnerabilities and recommendations for each library.



Conduct Manual Code Review

This is the phase where most critical vulnerabilities are usually found, including but not limited to vulnerabilities related to business logic, authorization, and authentication, which cannot be found through automated scans in most cases.

Different approaches can be used to analyze the code, depending on various factors, including the reviewer's preference.



Source and Sink

First, we need to know what source and sink mean, Source usually describes the data inputs in the application. 'Sink' is where that data will be handled or executed. Let's take an example:

A registration form that contains 'Country' and 'City' fields. When you select a country from the dropdown menu, it will send a request to inquire about all cities in that country. Assume the field name for the country is 'CountryID' and in the back end, there is a query to search for cities:
Here we might say that the dropdown menu is the entry point or the source of the user's input, to be more specific the request will be sent when we choose one of the countries which can be as follows:

http://test.com/getCities?CountryID=8

CountryID is the source in this scenario. In the backend, the class or controller that will handle this request can be something like this:

  http.HandleFunc("/getCities", func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("CountryID") // This is the source. 
            val, err := getCities(id)  
  })
The sink is the function where it will inquire the database:

func getCities(id string) ([]city, error) {
    rows, err := db.Query("SELECT cities FROM country WHERE country_Id = ?", id)
      // Do whatever you need to complete the query and return the value … 
}

One approach to code review is going from the source to the sink
The other approach is going from the sink to the source.
You can choose either way to examine the code, although the differences between these two methods can be summarized in the following points:

Source To Sink: This method can discover more vulnerabilities where the likelihood is high, but the severity is low.

Sink to Source: This method covers fewer vulnerabilities, with a low likelihood but higher severity



You can start based on the functionality, which means you go through each feature/function in the application. You can create a checklist to make this easy:
Another way is to divide functions based on the level of authorization required,
Moreover, you might begin the code review by examining modules, as code in many applications is often divided into separate modules or classes.
These suggestions aim to divide the review process, making it easier to follow.



Show your work (Reporting Phase)

This is the final phase where you have to provide a well-written document that shows your work and provides details of weaknesses and security flaws to the application owner. As a must-to-have in any code review findings:

1-Snippet of the vulnerable code.

2-Full file path containing the vulnerable code.

3-If applicable, the affected line number.

4-A detailed description of the vulnerability.

5-Clear steps on how to address and fix the vulnerability in the code.

6-As in any other security report, severity of the vulnerability.

Since it's not our topic to show how to write a security report, you can find the following example of a code review report:

https://joinup.ec.europa.eu/sites/default/files/inline-files/DLV%20WP6%20-01-%20KeePass%20Code%20Review%20Results%20Report_published.pdf



Conclusion

Finally, in this article, we provide an example of how to initiate a code review assessment. It's not necessary to follow these methods, but they can assist you in being more organized and to ensure covering all parts of the code.

References:

OWASP Code Review Guide

Advanced Web Attacks and Exploitation

Certified Application Security Engineer (CASE)

Micro Focus Fortify Static Code Analyzer User Guide

Share this blog
Follow us
Advance your skills by reading the latest blog created by our team.
Other Blogs