Security: Optional per-push TOTP verification for protected branches #193416
Replies: 1 comment
-
|
💬 Your Product Feedback Has Been Submitted 🎉 Thank you for taking the time to share your insights with us! Your feedback is invaluable as we build a better GitHub experience for all our users. Here's what you can expect moving forward ⏩
Where to look to see what's shipping 👀
What you can do in the meantime 💻
As a member of the GitHub community, your participation is essential. While we can't promise that every suggestion will be implemented, we want to emphasize that your feedback is instrumental in guiding our decisions and priorities. Thank you once again for your contribution to making GitHub even better! We're grateful for your ongoing support and collaboration in shaping the future of our platform. ⭐ |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
🏷️ Discussion Type
Product Feedback
Body
Problem
GitHub currently enforces 2FA at the credential issuance layer (login,
PAT creation, OAuth flow) but not at the credential use layer
(individual git operations). Once a PAT or SSH key exists on disk, an
attacker who obtains that credential — through malware, a misplaced
laptop, a leaked
.env, or a compromised CI secret — can push arbitrarycommits without any further challenge.
For maintainers of public, high-trust projects (language specs, security
tools, cryptography libraries, supply-chain-sensitive packages), this is
a real gap. The existing mitigations don't fully close it:
maintainer ends up reviewing their own PRs.
against the attacker signing new commits with a stolen on-disk GPG
key.
key theft: with
-O verify-requiredevery operation demands physicaltouch, and the private key never leaves the device. The residual risk
is narrow but real: during a legitimate user-initiated push, malware
on the same machine could attempt to substitute the content being
signed. This is a timing-and-substitution attack, not a passive
compromise, but it means hardware keys alone don't give a server-side
attestation that the user intended this specific push, to this
specific branch, of these specific commits.
maintainers won't adopt.
What's missing is a server-side gate that verifies a fresh 2FA
factor at the moment of
git push— something the attacker cannotbypass from the victim's machine alone, and that binds the approval to
the specific payload being pushed.
Proposal
A repo or org branch-protection setting:
UX sketch (no wire-protocol changes)
git push origin main.example:
remote: 2FA approval required. Approve at https://github.com/auth/push/XXXXAlternatively, a GitHub Mobile push notification is delivered for
the same pending approval.
approval server-side, keyed by
(user, repo, ref, commit SHAs).git push. GitHub, seeing a matching approval forthis exact payload, accepts the push.
The deliberate design choice here: no change to the git wire
protocol. The client isn't asked to understand a new auth flow; the
push is just a normal push that happens to succeed only if a
matching server-side approval exists. A future client-side convenience
(e.g.,
gh push, IDE integration, a credential helper that polls theapproval endpoint) can shorten this to a single command, but that's
an implementation layer above the protocol change — not a
prerequisite.
Scope knobs (essential)
feature branches unaffected.
GITHUB_TOKEN(already gated byworkflow permissions).
happens inside an authenticated session).
factors as existing 2FA UX, not a new factor type.
enforcement today.
Prior art
Within GitHub itself
The existing branch protection rule "Require approval of the most
recent push" already recognises that a push is a security-sensitive
event that may need additional verification beyond the initial
credential — it just implements that verification through a human
reviewer, not a cryptographic factor. This proposal is the same
pattern with a TOTP / WebAuthn gate instead of (or in addition to) a
second-person approval. Solo maintainers, who can't use the
second-person path, are currently unprotected against the
stolen-credential scenario.
GitHub's existing "sudo mode" — short-lived re-authentication
required for sensitive UI operations (changing email, deleting a
repo, issuing a PAT) — is exactly the primitive that would be
extended to
git pushunder this proposal.Outside GitHub
publish-with-2FArequires a fresh OTP for everypublish. Widely adopted by security-sensitive packages. Works
with automation via scoped automation tokens.
added.
--otpflag forgem push; MFA-requiredaccounts.
automation.
GitHub is the odd one out among major distribution platforms in
not offering a per-operation 2FA gate.
Why not just enforce it on all pushes?
Because that breaks CI, bots, and scripted workflows. The proposal
here is specifically:
No default behaviour changes. Existing repos and workflows continue
to work. Only repos where the owner explicitly opts in are affected
— and those owners have already accepted the friction by choosing
the setting.
Why this matters now
Credential-theft supply-chain attacks against source and package
ecosystems are a recurring category, not a one-off. Examples where
a stolen maintainer credential directly led to malicious publishes
or pushes:
pushed to the canonical
git.php.netrepository usingcompromised maintainer credentials. The incident directly
motivated PHP's migration of its source to GitHub.
compromised; malicious versions published.
takeovers.
stolen, backdoored versions published.
(Social-engineering attacks such as xz-utils 2024 and the 2024
polyfill.io domain-transfer incident are a different threat model
and would not be addressed by this proposal. The focus here is
specifically the credential-theft subclass.)
For every attack above, a per-operation 2FA gate bound to the
payload would have forced the attacker to also compromise the
victim's second factor — not just their machine or token. That is
the single hardening step missing from GitHub today that other
major publishing platforms already offer.
Beta Was this translation helpful? Give feedback.
All reactions