How to Use Branch Protection in Change Management
When developing software securely, many organizations have traditionally relied primarily on administrative security controls—i.e., policy and procedure documents that dictate change control processes and the different steps that need to be completed to remain compliant.
However, it’s easier than ever to test, package, and release software thanks to the proliferation of cloud, serverless, and container technologies, and this increased pace of software releases demands more technical controls to act as further guardrails to enforce change control requirements.
If you’ve been pondering what to do, you may have already landed on the most robust solution—a continuous integration and delivery (CI/CD) pipeline. But these pipelines often require gluing together multiple tools and require significant planning and engineering before they deliver value. As a highly experienced PCI QSA, we want to provide some alternative, simple steps you can take.
In this article, we’ll explain how branch protection can help you manage changes and keep your software development secure, complete with instructions on how to configure it within your environment.
Through the implementation of these measures, you’ll win twice—not only will you greatly improve your change management processes, but you’ll also keep your auditors happy.
Why Branch Protection?
A built-in feature of GitHub and GitLab, branch protection is an easy-to-use tool for enforcing workflows when pushing changes to a repository that does not pose a significant engineering challenge for your team.
Though we acknowledged it’s not as comprehensive as a full-blown CI/CD pipeline, branch protection does provide a lot of bang for your buck and can deliver value immediately.
But before it can do that, you need to configure it so that its protection rules can help you meet common security and compliance requirements, so let’s get into that.
How to Configure Branch Protection in GitHub
GitHub branch protection can be accessed within the settings of a given repository.
Though you’ll note there are a lot of configuration options, only a few are required to meet the change requirements we mentioned, the first of which is the branch name pattern that defines the branches where the rule will apply.
You can use a single branch name, such as “main,” or wildcard (fnmatch) syntax. Our example will apply to any branch that begins with the string “dev.”
The next batch of settings you need to set up will ensure that a pull request must occur before merging a commit to the target branch.
You can also specify a required number of approvers that a pull request needs before being merged, and you’ll need at least 2 to provide support for change control requirements.
At this point, you’ll also see mention of a CODEOWNERS file—this is another simple mechanism that provides granular control over approvers. We’ll come back to this later in more depth, so for now check the “Require review from Code Owners” box that will ensure that the approvers specified in the CODEOWNERS file will be required approvers on a pull request.
You’ll also need to check the box to “include administrators.” Allowing anyone to bypass controls greatly weakens their effectiveness, so checking this setting will ensure that the branch protection rules apply to everyone.
How to Configure Branch Protection in GitLab
As we move over to GitLab, the branch protection settings will be similar to GitHub, but you will need to visit two different menu areas to configure the needed options—first, navigate to the general settings of the GitLab project.
Within this menu, the minimum number of pull request approvers can be configured.
Next, head over to the “Repository” section in the project menu where the remainder of branch protection settings can be configured.
Again, GitLab’s settings are very similar to GitHub’s, so as you match all your settings to those you configured in GitHub, there’s one more step—take care to also set the “Allowed to push” option to “No one.” This will deny any direct commits, meaning that merge requests are required.
The Importance of the CODEOWNERS File
All these settings are important to get right, but while branch protection rules on their own can enforce multiple pull request approvers, they do leave a gap allowing a developer to select any approvers, which could lead to collusion, or to the appointment of approvers that are not “individuals who are knowledgeable in code review techniques and secure coding practices”—collusion is prohibited by PCI DSS, while knowledgeable approvers are required.
But a CODEOWNERS file can help plug that gap. Though just a regular text file placed in a repository, using it can help you define explicit users or teams as required reviewers based on the repository contents.
Take a look at this extremely simple CODEOWNERS file:
Even with these few lines, you can see the flexibility where file ownership can be assigned globally or to specific files and directories. Also, files can be assigned to specific users or teams.
Still, there’s something important to note when writing CODEOWNERS files—the last matching pattern takes the most precedence. In the above example, if the app/README.md file is modified, then only the @pdorczuk user would be added as a code approver, even though @echesec/security is a global owner.
If you have a large team, this could make CODEOWNERS files a little unwieldy to work with. Similarly, if there are dedicated change reviewers, those reviewers need to be included on every line to make sure they are always added as approvers. Take this simple use case:
- If you have a global change approver (1st line of the example above, you would need to add the @echesec/security team as approvers to any change in the repository.
Using CODEOWNDERS Files
To demonstrate how this works, we modified a file in our repository called foo.py. Based on CODEOWNERS, this file is owned by the @echesec/developers team. When I create a pull request, you can see that a code owner review is required, and the developer team is correctly called out as the code owner.
3 Benefits of Branch Protection
Now that everything is configured, let’s explore what these controls can do for us, including how branch protection can help you meet some common application change requirements.
1. Further Enforced Peer Review
Peer review requires someone other than the code author to review any changes before they are deployed into production. Though already enforced natively by GitHub and GitLab—both prevent the person who submits a pull request to be an approver—branch protection rules can further support peer review by requiring pull requests with a minimum number of reviewers, ensuring that more than one person is involved in the release process.
In the screenshot above, you can see that a pull request with 2 approvers is required before merging a commit—here below, the branch protection configuration there prevents users from directly committing to any “dev*” branch:
The subsequent pull request then requires two reviewers in order to close and merge it.
(Side Note: Since we have a CODEOWNERS file, you can also see that the developer team is added as a code owner above.)
2. Tightened Change Approval
Even though change approval is often more of an administrative than a technical control, branch protection rules can help here also. Change control procedures require changes to be reviewed by a formal change board, team, or specific individuals—this system is plugged into a change tracking system outside of a repository.
Still, change control procedures only work if everyone follows the rules—if not, while you may be able to retroactively uncover process violations, the damage could already be done.
But by explicitly requiring pull requests and approvals from authorized change approvers, branch protection rules in conjunction with a CODEOWNERS file can help ensure that the approval process cannot be circumvented and that appropriate parties are notified.
3. Enforced Separation of Duties
PCI DSS says this about separation of duties:
“In environments where one individual performs multiple roles (for example application development and implementing updates to production systems), duties should be assigned such that no one individual has end-to-end control of a process without an independent checkpoint. For example, assign responsibility for development, authorization and monitoring to separate individuals.”
From a source code perspective, branch protection rules can enforce a separation of duties—by requiring multiple pull request approvers, you create an independent checkpoint. Multiple required reviewers ensure that collusion would be required to sneak malicious code into production.
Other Branch Protection Considerations
Declarative Branch Protection
For any secure or regulated environment, Infrastructure as Code is a critical piece, and if your organization has adopted a DevOps methodology, the security of your Git repository becomes even more important.
For this reason, you may consider expanding your infrastructure as code to include GitHub itself. If so, Terraform has both GitHub and GitLab providers where you can configure the same branch protection settings that we set earlier in the web portals:
And in Gitlab:
Branch protection only works if it is enabled, so it’s important to configure the rules when repositories are created and monitor audit logs for any modifications to the rules.
Luckily, as most cloud service providers do, both GitHub and Gitlab keep audit logs of any configuration changes that occur whether through the web console or API. These audit logs can be used to detect any modifications to branch protection rules.
Want to Learn More About PCI DSS Compliance?
In the interest of full transparency, there are even more application change requirements—such as static code analysis scans and other automated testing and deployment—that can be met using a CI/CD pipeline.
But as you’ve just read, branch protection and CODEOWNERS files can form a solid foundation to enforce your organization’s change management requirements, whether used on their own or in conjunction with said CI/CD pipeline.
To continue learning more about your options within the payment security compliance space, make sure you check out our other content on varying subjects across this complicated industry:
About PHIL DORCZUK
Phil Dorczuk is a Senior Associate with Schellman. Prior to joining Schellman, LLC in 2013, Phil worked as a PCI DSS auditor with Coalfire Systems and a consultant at GTRI. At Coalfire, Phil specialized in PCI DSS audits and gap assessments and at GTRI specialized in Cisco network equipment installation and configuration.