Developing More Secure Applications with GitLab Security

Gonzalo Gentile
Globant
Published in
6 min readDec 29, 2023
Source: linkedin.com

The purpose of this article is to explain and show the different security features that GitLab Security & Compliance offers to an organization so development and infrastructure teams can use them to improve their security posture. These features are available in the GitLab Ultimate version.

GitLab Security Features

We will start by mentioning the security features of GitLab, briefly explaining each of them:

  • Secret Detection: scans your repository to help prevent your secrets from being exposed.
  • SAST: checks your source code for known vulnerabilities, supporting a lot of programming languages and frameworks.
  • Infrastructure as Code: scans your IaC configuration files for known vulnerabilities.
  • Dependency Scanning: scans your application’s dependencies for known vulnerabilities, including nested dependencies.
  • Container Scanning: analyzes your application’s image, looking for vulnerabilities.
  • DAST: Scan the application at runtime and look for vulnerabilities to which it is exposed, and possible attackers can try to exploit it. Needs the application up and running.
  • API Analyzer: scan your APIs looking for vulnerabilities.
  • API Fuzzing: fuzz testing sends unexpected values to the application to cause unexpected behavior and errors in the API backend. Needs the application up and running.

How Do We Enable These Features?

We have mainly two options:

  • Editing your .gitlab-ci.yml and including the different security GitLab templates that enable these capabilities for you. Here, you can find GitLab’s template details and dive deeper into this.
  • Just be part of a Security Policy Scan, and this policy will do these scans for you without editing your repository/files! Yes, when your pipeline is executed, you will see additional security jobs that will do this scan automatically, and your .gitlab-ci.yml will be the same as always without any modification. Sounds good, right? I will explain this centralized approach in the upcoming minutes.

Below are the implementation details depending on which option you choose.

Editing your .gitlab-ci.yml

Including the following code snippets in your YAML file will add the different features available in GitLab.

Secret Detection

The following line must be added to scan secrets:

include:
- template: Jobs/Secret-Detection.gitlab-ci.yml

SAST

Note that for SAST runs, the test stage is required:

include:
- template: Jobs/SAST.gitlab-ci.yml

IaC

Same requirement as above, test stage is required:

include:
- template: Security/SAST-IaC.gitlab-ci.yml

Dependency Scanning

Same requirement as above, test stage is required:

include:
- template: Security/Dependency-Scanning.gitlab-ci.yml

Container Scanning

Main prerequisites:

  • The test stage is required.
  • Build and push the Docker image to your project’s container registry.
  • In case you’re using a third-party container registry, provide authentication credentials through the CS_REGISTRY_USER and CS_REGISTRY_PASSWORD configuration variables. More details here.
include:
- template: Security/Container-Scanning.gitlab-ci.yml

DAST

Prerequisites:

  • Target application deployed and accessible through GitLab.
  • dast stage added to the CI/CD YAML file.
include:
- template: DAST.gitlab-ci.yml

dast:
variables:
DAST_WEBSITE: "https://example.com"
DAST_BROWSER_SCAN: "true"

API Analyzer

For this scan, you need to specify:

  • Based on the intensity, you can establish the proper DAST_API_PROFILE value.
  • Depending on the API specification, you need to specify the location of the spec. Example for OpenAPI: DAST_API_OPENAPI. More examples like GraphQL and HAR here.
  • Finally, specify the target API instance’s base URL.
include:
- template: DAST-API.gitlab-ci.yml

variables:
DAST_API_PROFILE: Quick
DAST_API_OPENAPI: test-api-specification.json
DAST_API_TARGET_URL: https://example.com/

API Fuzzing

You must define a fuzz stage. Regarding the FUZZAPI_PROFILE, it tells you the intensity of the scanning tests; here you can view the rest of the available profiles:

include:
- template: API-Fuzzing.gitlab-ci.yml

variables:
FUZZAPI_PROFILE: Quick-10
FUZZAPI_OPENAPI: https://example.com/test-api-specification.json
FUZZAPI_TARGET_URL: https://example.com/

Using security policies

There are 2 types of security policies:

  • Scan execution policy: they serve to ensure that a set of configured security scans will run automatically, being able to run through a scheduler or in the execution of a specific pipeline.
  • Scan result policy: allows you to configure actions based on the findings obtained. Example: Require two approvals from the Application Security team in case the scan detects a critical vulnerability in an open merge request.

How to enable the scan execution policy?

The Security policy can be applied at the group or project level. If you apply at the group level, all the projects inside the group will have this policy enabled by default, inheriting this functionality.

Relationships between policies and groups/projects

Before enabling the policy, it is recommended to create a separate project at the same level as the project/group to be scanned and separate this security policy project and the projects that contain the developments to be scanned.

To create an execution policy, we must go to the Secure > Policies > New Policy section and select Scan execution policy:

Policies section in GitLab

The easiest way to create a policy is by using the rule mode, where we can select the type of scan we want to perform and under what conditions it should be executed. In this example, we are configuring a SAST scan to be triggered every time a pipeline runs for any branches.

Once the policy is finalized, we implement this change through a merge request:

Scan execution policy creation in GitLab

This will create the following folder structure with the security policy in YAML format with detailed specifications:

Policy.yml file creation

Advanced Setup — How To Create Custom Rules And Override Variables?

Every security scan offered by GitLab comes with a set of variables with default values ​​that can be changed. Additionally, each of the different security scans has a set of default rules configured to detect vulnerabilities. Both the variables and the detection rules can be modified. In this section, I will mention some useful customization options that will help you leverage these features based on your needs.

Advanced Secret Detection

You can edit the current configuration or even create your configuration file detailing what information you would like to detect or skip, using regex expressions to scan these known patterns.

In this example, we are disabling the following rule since we would rather not receive findings. To achieve this, you need to create the secret-detection-ruleset.toml file in the .gitlab directory and match the proper rule from the default ruleset:

[secrets]
[[secrets.ruleset]]
disable = true
[secrets.ruleset.identifier]
type = "gitleaks_rule_id"
value = "RSA private key"

Here, you have more examples to consider.

Advanced SAST

For SAST scans, additional scans can be added depending on the technology and code behind them. A very useful example is to add the following code to the scan to detect incorrect configurations in the k8s manifests. You need to set SCAN_KUBERNETES_MANIFESTS to “true” to enable the Kubesec analyzer. In .gitlab-ci.yml, define:

include:
- template: Security/SAST.gitlab-ci.yml

variables:
SCAN_KUBERNETES_MANIFESTS: "true"

Advanced DAST

One of the features available to set up is authentication. The alternatives that GitLab offers us are very useful, due to not all login processes being the same. Passing authentication, the analyzer can test as much of the application as possible when searching for vulnerabilities.

GitLab supports different ways to authenticate; this will depend on how the application was developed: single-step login forms, multistep login forms, single sign-on, and authenticating to URLs outside the configured target URL.

Let’s see an example: a single-step login form has all login form elements on a single page. Configuration requires the following variables to be defined for the DAST job:

include:
- template: DAST.gitlab-ci.yml

dast:
variables:
DAST_WEBSITE: "https://example.com"
DAST_AUTH_URL: "https://example.com/login"
DAST_USERNAME_FIELD: "css:[name=username]"
DAST_PASSWORD_FIELD: "css:[name=password]"
DAST_SUBMIT_FIELD: "css:button[type=submit]"

Caveats: Don’t define username or password in the YAML file!

Conclusion

I recommend using this approach in organizations that do not have commercial security tools, in development teams that are just doing their first steps in secure development, and, of course, have the GitLab Ultimate license to have these available features.

This approach is a straightforward way to start including security in developments where the tools and results are managed with git, a tool that the developer already knows and uses daily. With this approach, there is no need to add a new third-party security tool to development teams, thus facilitating its adoption.

References

--

--