Skip to content

Git Repository Security

7 min readGit Security & DevSecOps · Repository Security

Everything else in this pillar assumes an answer to one question: who can change this repository, and how do you know a change came from where it says it did?

If that answer is loose, no amount of scanning downstream compensates. A vulnerability scanner reading a branch that anyone can force-push is auditing a moving target.

Start with Secure Repository Configuration

Repository security has the best return on effort in the pillar, for an unglamorous reason: most of it is configuration, and configuration is cheap, reversible and testable.

Turning on required reviews takes a minute and closes the “one compromised account pushes to main” path permanently. Replacing a long-lived personal access token with a scoped GitHub App installation removes a credential that would otherwise work from anywhere, forever. Neither requires an engineering project.

The catch is that configuration drifts silently. Nothing in GitHub tells you that the bypass list on your ruleset has grown, or that a deploy key added for a one-off migration is still there. This cluster is as much about review as it is about initial setup.

Threat. An identity with write access to the repository is used by someone who should not have it — or by an automation with broader access than the job requires — to introduce a change that reaches the default branch.

Attack surface. Personal accounts and their credentials (passwords, SSH keys, personal access tokens, session cookies). Machine identities (deploy keys, GitHub Apps, Actions tokens). The repository’s own settings, including who may bypass its rules. Forks and the fork network. Anything that can push a tag.

Impact. Direct write access to a repository is code execution on every machine that builds it, plus whatever the CI system’s credentials reach. In a repository that deploys, it is production.

Control. Least privilege on both human and machine identities; authentication that cannot be replayed from a stolen text file; repository rules that constrain what any identity may do to protected refs; commit signing so authorship claims can be verified rather than asserted.

Verification. Attempt the thing the control prevents. Push directly to a protected branch. Try the token you believe is scoped to one repository against another. Check that a commit you did not sign shows as unverified.

Identity vs credential. An identity is who you are; a credential is how you prove it. One identity may hold several credentials, and revoking one does not revoke the others — a fact that matters enormously during an incident.

Human vs machine identity. They fail differently and should be managed differently. Humans forget, get phished and leave the company. Machines do not forget, but they also never notice that their access is excessive and never hand it back.

Policy vs enforcement. A written rule that everything goes through review is policy. A ruleset that rejects a direct push is enforcement. Only the second one survives a bad day.

Bypass is part of the control. Every enforcement mechanism has an escape hatch, because production sometimes breaks. The security question is never “is there a bypass” — it is who holds it, whether using it is visible, and whether anyone reads that record.

  1. Lesson 1: 01. Secure Repository ConfigurationA practical hardening baseline for a Git repository on GitHub — access, authentication, branch and tag policy, commit authenticity, secrets, ignored files, hooks.Intermediate14 min read
  2. Lesson 2: 02. Branch Protection for SecurityBranch protection read as a security control — the attack path each rule closes, what bypass really costs, why administrators are the weak point, and what no rule can touch.Intermediate13 min read
  3. Lesson 3: 03. Rulesets for SecurityRulesets as security policy — branch, tag and push rules, required code scanning results, bypass actors, layering, evaluate mode and organisation-wide enforcement.Intermediate → Advanced14 min read
  4. Lesson 4: 04. Signed Git CommitsWhat a commit signature actually proves, how GitHub verifies it, SSH versus GPG signing, vigilant mode, enforcement through rulesets, key compromise and revocation.Intermediate13 min read
  5. Lesson 5: 05. Git SSH KeysSSH authentication for Git done securely — current key types, passphrases and ssh-agent, host verification, multiple accounts, deploy keys, rotation and what to do when a key is exposed.Intermediate13 min read
  6. Lesson 6: 06. GPG / OpenPGP SigningOpenPGP signing for Git — key generation, expiry, subkeys, revocation certificates, GitHub registration, protecting the private key, and an honest comparison with SSH signing.Intermediate13 min read
  7. Lesson 7: 07. Git CredentialsHow Git obtains and stores credentials for HTTPS remotes — credential helpers, OS keychains, the plaintext store, token scope and expiry, credentials in automation, and safe rotation.Intermediate14 min read
  8. Lesson 8: 08. Least-Privilege GitHub AccessAn access architecture for GitHub: repository roles, teams, outside collaborators, deploy keys and GitHub Apps — who should have what, and how to audit it.Intermediate → Advanced14 min read
  9. Lesson 9: 09. Fine-Grained GitHub TokensKeep GitHub tokens from becoming a liability: expiry that works without you, safe storage, rotation and revocation, org policy, auditing, and when a GitHub App is the right answer instead.Intermediate13 min read

The order is deliberate. Lesson 1 sets a baseline you can apply to a repository today. Lessons 2 and 3 cover enforcement — branch protection and rulesets, approached as security policy rather than as workflow convenience. Lessons 4 to 6 are about authenticity and authentication: what a signature proves, and how to hold keys. Lessons 7 to 9 are credentials and access architecture, ending with the identity model that the rest of the pillar assumes.

Controls are easier to prioritise when you can see what each one actually blocks. Nothing in this table stops everything, and the gaps are the point.

ControlStopsDoes not stop
Required pull request reviewsAn individual pushing unreviewed code to a protected branchA reviewer approving without reading; anyone with bypass
Block force pushesHistory being rewritten under people who already pulled itOrdinary commits that introduce the same change
Restrict deletionsA protected branch or tag disappearingThe branch being updated to something bad
Required status checksMerging a change whose checks failedMerging a change whose checks are inadequate
Require signed commitsCommits with unverifiable authorship reaching a protected refA legitimately signed commit containing a backdoor
Fine-grained token scopingOne leaked token reaching every repositoryThat token being used within its scope
SSH key with a passphraseAn unattended copy of the key file being usable directlyA key exported from a running agent
Removing admin bypassRules being quietly stepped around during a hurrySomeone re-granting themselves admin

Read the right-hand column as the specification for the next layer. “Require signed commits” not stopping a malicious signed commit is exactly why review exists; review not stopping an unreviewed direct push is exactly why the ruleset exists.

If you are applying this to a real repository rather than reading it through, the order that produces the most benefit per unit of disruption:

  1. Protect the default branch. Require a pull request, block force pushes, restrict deletions. This is the single highest-value change and it is three checkboxes.
  2. Look at the bypass list. A rule that organisation owners and repository admins can step around is a suggestion. Decide deliberately who keeps that, and know that using it is recorded.
  3. Inventory the machine identities. Deploy keys, GitHub Apps, Actions secrets, personal access tokens belonging to people who set up an integration once. This is where the surprises are.
  4. Set token expiry. A credential with no expiry is a credential you will forget you issued.
  5. Protect release tags. Tag rules are frequently skipped and tags are what downstream consumers actually pin to.
  6. Turn on signing for the people who publish. Full-team signing is a bigger cultural change; maintainers who cut releases are a smaller and higher-value set.
  7. Schedule the review. Every item above drifts. A control with no review date is a control with a decay curve you are not watching.

Steps 1 to 5 are an afternoon. Steps 6 and 7 are the ones organisations skip and then regret.

Protecting main and forgetting tags. Consumers pin to v2, not to a branch. A movable release tag means the version somebody installed last week may not be the code that tag points at today.

Treating “admins can bypass” as harmless. It usually is, right up until an admin account is the one that is compromised — at which point every rule in the repository evaluates to “allowed”.

Confusing repository roles with capability. Write access is not one thing. It includes pushing branches, creating tags, editing workflow files and, in many configurations, triggering deployments.

Auditing settings instead of outcomes. Reading a settings page confirms the configuration. It does not confirm that the configuration does what you think, and the two diverge more often than anyone expects.

You do not need those pages memorised. This cluster re-establishes what it needs and links back for mechanics rather than repeating them.

What this cluster deliberately does not cover

Section titled “What this cluster deliberately does not cover”

Account security basics. Two-factor authentication, passkeys and account recovery are covered in GitHub Account Setup. They matter — an unprotected account makes everything here moot — but they are not repository controls.

Workflow permissions. permissions: in a workflow file, and the scoping of GITHUB_TOKEN, belong to Least-Privilege Permissions in Pillar 4. Lesson 8 here covers how Actions permissions fit the broader access model, and links there for the YAML.

Organisation-wide governance products. Enterprise policy, SAML, SCIM provisioning and audit log streaming are organisation administration rather than repository engineering. Lesson 8 states where they belong in the model without turning into an enterprise administration guide.

Repository Security establishes who can change code and whether a change’s origin is verifiable. The next cluster deals with what must never be in the code in the first place.

That ordering is not arbitrary. Secret incident response involves rewriting history and force-updating branches — operations that require you to already understand the protection rules you are about to work around, and to have somewhere to put the bypass record.

Next cluster — Secret Security