Git is a distributed version control system that helps developers manage and track changes in their codebase by organizing them into repositories. Several popular Git repository management platforms exist, including GitHub, GitLab, Bitbucket, and Azure DevOps, each offering features and integrations to support various development workflows.
Securing Git repositories is crucial for keeping your data and code safe. It means safeguarding sensitive information, preserving code integrity, and controlling access. Protecting these digital assets against unauthorized access and tampering is essential to mitigate data breaches, code vulnerabilities, and compliance risks.
In short, you need to use MFA to secure the authentication. You need to monitor changes made to your code through code scanning, protection rules on the main branch, and the use of code owners.
You should control code integrity when signing a commit. It’s also important to protect access to your repositories by using user groups and preventing the creation of public repositories.
Here are more details on each best practice.
Our Recommendation: You should enable MFA (Multi Factor Authentication) and enforce it for every user in your organization.
Risks addressed by the recommendation: There are three factors for authentication: what you are (a fingerprint, Face ID), what you know (a password), or what you have (a phone to receive a code). Traditional authentication systems typically use only one of these factors, often a password.
With only one authentication factor, there is a high risk of your accounts being compromised. If someone steals what you have (your phone) or discovers what you know (your password), they can authenticate as you.
When we talk about MFA (multi-factor authentication), we combine at least two factors for authentication. For example, to authenticate yourself to a system, you first enter a password and a code you received on your phone. This significantly reduces the risks of an account being compromised.
It's crucial to enforce MFA within your organization, especially when considering the importance of your repositories. A single compromised account can compromise all of your repositories, and they contain code that drives your applications or manages your infrastructure. Failing to secure them adequately can lead to attacks. For instance, an attacker could insert a backdoor into your app's code.
They could also tamper with your infrastructure through Infrastructure as Code (IaC), causing damage such as deleting resources or adding ones that could be exploited to infiltrate your systems and gain unauthorized access. The consequences of such security breaches could be devastating!
Our Recommendation: You should manage access to your repositories by creating groups and associating roles with each group for each repository. It allows you to configure which group has which permissions on which repository (read rights, push rights to the repository, administrative rights, etc.). For example, these are the default roles on GitHub that you can associate with a group:
You can also define custom repository roles in your organization. This approach aligns with the principle of least privilege and enhances security.
Risks addressed by the recommendation: The most significant risk associated with not implementing groups for repository access is the lack of granular control. Without groups, each user must be managed individually for each repository, a process that quickly becomes unmanageable in larger organizations. This increases the likelihood of human errors, such as misassigning permissions, which can lead to data leaks or security breaches.
Moreover, implementing groups for repository access plays a pivotal role in segregating rights and responsibilities. It ensures that each team or individual is granted access only to the repositories essential for their specific roles, preventing over-privileged access and reducing the risk of unauthorized changes.
This separation of responsibilities enhances both security and operational efficiency, as teams can focus on their designated tasks without inadvertently affecting unrelated parts of the enterprise.
Our Recommendation: You should enable the following protection rules on the default branch:
Risks addressed by the recommendation: Enforcing these protection rules on the main branch helps mitigate unauthorized changes. Disallowing direct pushes to the main branch prevents individuals from making unauthorized changes to critical code, reducing the risk of malicious code injection or accidental errors.
Mandating reviews by designated teams establishes a transparent ownership structure, ensuring only authorized individuals or groups can approve and merge code. This enhances accountability and security by preventing unauthorized personnel from making critical decisions regarding the codebase.
Our Recommendation: We recommend setting up a CODEOWNERS file with care. This file designates trusted individuals or teams responsible for approving changes to specific repository files. It's helpful if you have specific sensitive files that you don't want the repository's owner team to be able to approve changes to, but rather, a dedicated team for these files.
Risks addressed by the recommendation: CODEOWNERS files allow for granular control over protecting specific files or processes rather than applying it to the entire repository. For example, here's a concrete scenario we encountered in a project: developers were owners of a repository that contained a file defining an IaC scan with Checkov.
The developers would modify this file to deactivate and bypass the security scan. In response, we implemented a CODEOWNERS file for this specific file, ensuring that only the security team could approve its modifications.
This approach empowers development teams to control their repositories and pull requests through internal peer review while safeguarding critical aspects like security scanning and code checks. It addresses risks associated with unauthorized code changes.
It ensures that protective measures remain intact, even if development teams might be tempted to disable them due to perceived restrictions or limitations.
Our Recommendation: You should require your developers to sign their commits before pushing them. To sign a commit, a developer uses a private key to encrypt the commit's content. This feature can be easily implemented on GitHub or GitLab using the “require signed commits” / ”reject un-signed commits” protection on branches.
Helpful tip: Commits can be signed with GPG in Git, a free open-source software program that provides cryptographic privacy and authentication. Git does not care about the location of the GPG keys used for signing commits. This means your developers can import a signature key onto their YubiKey, enabling them to sign their commits using it!
Risks addressed by the recommendation: Committing changes without proper verification can lead to malicious code injection. An attacker could push code containing vulnerabilities while impersonating a legitimate developer.
Furthermore, enforcing proper verification and signing of commits helps protect against unauthorized modifications to the Git history. Git history is invaluable for tracing the evolution of the codebase, understanding past changes, and, in some cases, conducting forensic analysis in the event of security incidents.
Failing to secure the Git history increases the risk of tampering, which could destroy valuable historical data.
Therefore, adopting proper commit verification practices safeguards against malicious code and helps preserve the integrity and reliability of your project's Git history.
Our Recommendation: We recommend disabling the creation of public repositories or forking (a fork is a duplicate of a repository under our control).
Risks addressed by the recommendation: The creation of public repositories can expose sensitive information. Disabling this can help prevent a configuration error.
As for forking, it can complicate tracking security issues because vulnerabilities in the original repository may also exist in its forks.
If you don't want to disable these features, setting the default visibility for new repositories to private and keeping track of all forks is essential.
Our Recommendation: We recommend scanning your code for vulnerabilities. There are several scans that it is crucial to set up.
You must enforce scans on all new code by setting up a workflow triggered on pull request events. You can use GitHub Actions or GitLab CI/CD to do so.
Risks addressed by the recommendation: Not having code scanning on your repositories can pose risks such as introducing new security issues or leaking secrets, potentially leading to infrastructure compromise. It is important to enforce these security scans so developers cannot bypass them when pushing new code.
By following the best practices presented in this article, you can secure your repositories and improve the overall security of your infrastructure.