title: Unsigned Commits
url: https://blog.glyph.im/2024/01/unsigned-commits.html
hash_url: ce5fdc61fd66cdb9ce548fb543eba986
archive_date: 2024-01-25
og_image: https://blog.glyph.im/images/back9.png
description: Deciphering Glyph, the blog of Glyph Lefkowitz.
favicon: https://blog.glyph.im/images/favicon.ico
language: en_US

<p>I am going to tell you why I don’t think you should sign your Git commits, even
though doing so with SSH keys is now easier than ever.  But first, to
contextualize my objection, I have a brief hypothetical for you, and then a bit
of history from the evolution of security on the web.</p>
<hr>
<p><img alt="paper reading “Sign Here:” with a pen poised over it" src="https://blog.glyph.im/images/just-sign-here.jpeg"></p>
<hr>
<p>It seems like these days, everybody’s signing all different kinds of papers.</p>
<p>Bank forms, permission slips, power of attorney; it seems like if you want to
securely validate a document, you’ve gotta sign it.</p>
<p>So I have invented a machine that automatically signs every document on your
desk, just in case it needs your signature.  Signing is good for security, so
you should probably get one, and turn it on, just in case something needs your
signature on it.</p>
<p>We also want to make sure that verifying your signature is easy, so we will
have them all notarized and duplicates stored permanently and publicly for
future reference.</p>
<p>No?  Not interested?</p>
<hr>
<p>Hopefully, that sounded like a silly idea to you.</p>
<p>Most adults in modern civilization have learned that signing your name to a
document has an <em>effect</em>.  It is not merely decorative; the words in the
document being signed have some specific meaning and can be enforced against
you.</p>
<p>In some ways the metaphor of “signing” in cryptography is bad.  One does not
“sign” things with “keys” in real life.  But here, it is spot on: a
cryptographic signature can have an effect.</p>
<p>It should be an <em>input</em> to some software, one that is acted upon.  Software
does a thing differently depending on the presence or absence of a signature.
If it doesn’t, the signature probably shouldn’t be there.</p>
<hr>
<p>Consider the most venerable example of encryption and signing that we all deal
with every day: HTTPS.  Many years ago, browsers would happily display
unencrypted web pages.  The browser would also encrypt the connection, if the
server operator had paid for an expensive certificate and correctly configured
their server. If that operator messed up the encryption, it would pop up a
helpful dialog box that would tell the user “This website did something wrong
that you cannot possibly understand.  Would you like to ignore this and keep
working?” with buttons that said “Yes” and “No”.</p>
<p>Of course, these are not the precise words that were written.  The words, as
written, said things about “information you exchange” and “security
certificate” and “certifying authorities” but “Yes” and “No” were the words
that most users <em>read</em>.  Predictably, most users just clicked “Yes”.</p>
<p>In the usual case, where users ignored these warnings, it meant that no user
ever got meaningful security from HTTPS.  It was a component of the web stack
that did nothing but funnel money into the pockets of certificate authorities
and occasionally present annoying interruptions to users.</p>
<p>In the case where the user carefully read and honored these warnings in the
spirit they were intended, adding any sort of transport security to your
website was a potential liability.  If you got everything perfectly correct,
nothing happened except the browser would display a picture of a <a href="https://www.wired.com/2016/11/googles-chrome-hackers-flip-webs-security-model/">small green
purse</a>. If
you made any small mistake, it would scare users off and thereby directly harm
your business.  You would only want to do it if you were doing something that
put a big enough target on your site that you became unusually interesting to
attackers, or were required to do so by some contractual obligation like credit
card companies.</p>
<p>Keep in mind that the second case here is the <em>best</em> case.</p>
<p>In 2016, the browser makers noticed this problem and started taking some
<a href="https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html">pretty aggressive
steps</a>
towards actually enforcing the security that HTTPS was supposed to provide, by
fixing the user interface to do the right thing.  If your site didn’t have
security, it would be shown as “Not Secure”, a subtle warning that would
gradually escalate in intensity as time went on, correctly incentivizing site
operators to adopt transport security certificates.  On the user interface
side, certificate errors would be significantly harder to disregard, making it
so that users who didn’t understand what they were seeing would actually be
stopped from doing the dangerous thing.</p>
<p>Nothing fundamental<sup id="fnref:1:unsigned-commits-2024-1"></sup> changed about the technical aspects of the
cryptographic primitives or constructions being used by HTTPS in this time
period, but <em>socially</em>, the meaning of an HTTP server signing and encrypting
its requests changed a lot.</p>
<hr>
<p>Now, let’s consider signing Git commits.</p>
<p>You may have heard that in some abstract sense you “should” be signing your
commits.  GitHub puts a little green “verified” badge next to commits that are
signed, which is neat, I guess.  They provide “security”.  1Password provides a
<a href="https://developer.1password.com/docs/ssh/git-commit-signing/">nice UI</a> for
setting it up. If you’re not a 1Password user, GitHub itself recommends you
<a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key">put in just a few lines of
configuration</a>
to do it with either a GPG, SSH, or even an S/MIME key.</p>
<p>But while GitHub’s documentation quite lucidly tells you <em>how</em> to sign your
commits, its explanation of
<a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification">why</a>
is somewhat less clear.  Their purse is the word “Verified”; it’s still green.
If you enable “<a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits">vigilant
mode</a>”,
you can make the blank “no verification status” option say “Unverified”, but
not much else changes.</p>
<p>This is like the old-style HTTPS verification “Yes”/“No” dialog, except that
there is not even an interruption to your workflow.  They might put the
“Unverified” status on there, but they’ve gone ahead and clicked “Yes” for you.</p>
<p>It is tempting to think that the “HTTPS” metaphor will map neatly onto Git
commit signatures.  It was bad when the web wasn’t using HTTPS, and the next
step in that process was for <a href="https://en.wikipedia.org/wiki/Let%27s_Encrypt">Let’s
Encrypt</a> to come along and for
the browsers to fix their implementations.  Getting your certificates properly
set up in the meanwhile and becoming familiar with the tools for properly doing
HTTPS was unambiguously a <em>good</em> thing for an engineer to do.  I did, and I’m
quite glad I did so!</p>
<p>However, there is a significant difference: signing and encrypting an HTTPS
request is ephemeral; signing a Git commit is functionally permanent.</p>
<p>This ephemeral nature meant that errors in the early HTTPS landscape were
easily fixable.  Earlier I mentioned that there was a time where you might
<em>not</em> want to set up HTTPS on your production web servers, because any small
screw-up would break your site and thereby your business.  But if you were
really skilled and you could see the future coming, you could set up
monitoring, avoid these mistakes, and rapidly recover.  These mistakes didn’t
need to badly break your site.</p>
<p>We <em>can</em> extend the analogy to HTTPS, but we have to take a detour into one of
the more unpleasant mistakes in HTTPS’s history: <a href="https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning">HTTP Public Key
Pinning</a>, or “HPKP”.
The idea with HPKP was that you could publish a record in an HTTP header where
your site commits<sup id="fnref:2:unsigned-commits-2024-1"></sup> to using certain certificate authorities for a period of
time, where that period of time could be “forever”.  Attackers gonna attack,
and <a href="https://scotthelme.co.uk/using-security-features-to-do-bad-things/#usinghpkpforevil">attack they
did</a>.
Even without getting attacked, a site could easily commit “HPKP Suicide” where
they would pin the wrong certificate authority with a long timeline, and their
site was effectively gone for every browser that had ever seen those pins.  As
a result, after a few years, HPKP was <a href="https://scotthelme.co.uk/hpkp-is-no-more/">completely removed from all
browsers</a>.</p>
<p>Git commit signing is even worse.  With HPKP, you could easily make terrible
mistakes with permanent consequences even though you knew the <em>exact</em> meaning
of the data you were putting into the system at the time you were doing it.
With signed commits, you are saying something permanently, but you don’t really
know <em>what</em> it is that you’re saying.</p>
<hr>
<p><em>Today</em>, what is the benefit of signing a Git commit?  GitHub might present it
as “Verified”.  It’s worth noting that only <em>GitHub</em> will do this, since they
are the root of trust for this signing scheme.  So, by signing commits and
registering your keys with GitHub, you are, at best, helping to lock in GitHub
as a permanent piece of infrastructure that is even harder to dislodge because
they are not only where your code is stored, but also the arbiters of whether
or not it is trustworthy.</p>
<p><em>In the future</em>, what is the possible security benefit?  If we all collectively
decide we want Git to be more secure, then we will need to meaningfully treat
signed commits differently from unsigned ones.</p>
<p>There’s a long tail of unsigned commits several billion entries long.  And
those are in the permanent record as much as the signed ones are, so future
tooling will have to be able to deal with them.  If, as stewards of Git, we
wish to move towards a more secure Git, as the stewards of the web moved
towards a more secure web, we do <em>not</em> have the option that the web did.  In
the browser, the meaning of a plain-text HTTP or incorrectly-signed HTTPS site
changed, in order to encourage the site’s operator to <em>change</em> the site to be
HTTPS.</p>
<p>In contrast, the meaning of an unsigned commit cannot change, because there are
zillions of unsigned commits lying around in critical infrastructure and we
need them to remain there.  Commits cannot meaningfully be changed to become
signed retroactively.  Unlike an online website, they are part of a historical
record, not an operating program.  So we cannot establish the difference in
treatment by changing how unsigned commits are treated.</p>
<p>That means that tooling maintainers will need to provide some difference in
behavior that provides some incentive.  With HTTPS where the binary choice was
clear: don’t present sites with incorrect, potentially compromised
configurations to users.  The question was just <em>how</em> to achieve that.  With
Git commits, the difference in treatment of a “trusted” commit is far less
clear.</p>
<p>If you will forgive me a slight straw-man here, one possible naive
interpretation is that a “trusted” signed commit is that it’s OK to run in CI.
Conveniently, it’s not simply “trusted” in a general sense.  If you signed it,
it’s trusted to be <em>from you</em>, specifically.  Surely it’s fine if we bill the
CI costs for validating the PR that includes that signed commit to your GitHub
account?</p>
<p>Now, someone can piggy-back off a 1-line typo fix that you made on top of an
unsigned commit to some large repo, making you implicitly responsible for
transitively signing all unsigned parent commits, even though you haven’t
looked at any of the code.</p>
<p>Remember, also, that the only central authority that is practically trustable
at this point is your GitHub account.  That means that if you are using a
third-party CI system, even if you’re using a third-party Git host, you can
only run “trusted” code if GitHub is online and responding to requests for its
“get me the trusted signing keys for this user” API.  This also adds a lot of
value to a GitHub credential breach, strongly motivating attackers to sneakily
attach their own keys to your account so that their commits in unrelated repos
can be “Verified” by you.</p>
<p>Let’s review the pros and cons of turning on commit signing <em>now</em>, before you
know what it is going to be used for:</p>
<table>
<thead>
<tr>
<th align="left">Pro</th>
<th align="left">Con</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Green “Verified” badge</td>
<td align="left"><strong>Unknown, possibly unlimited future liability</strong> for the consequences of running code in a commit you signed</td>
</tr>
<tr>
<td align="left"><p></p></td>
<td align="left">Further implicitly cementing GitHub as a  centralized trust authority in the open source world</td>
</tr>
<tr>
<td align="left"></td>
<td align="left">Introducing unknown reliability problems into infrastructure that relies on commit signatures</td>
</tr>
<tr>
<td align="left"></td>
<td align="left">Temporary breach of your GitHub credentials now lead to potentially permanent consequences if someone can smuggle a new trusted key in there</td>
</tr>
<tr>
<td align="left"></td>
<td align="left">New kinds of ongoing process overhead as commit-signing keys become new permanent load-bearing infrastructure, like “what do I do with expired keys”, “how often should I rotate these”, and so on</td>
</tr>
</tbody>
</table>
<p>I feel like the “Con” column is coming out ahead.</p>
<hr>
<p>That probably seemed like increasingly unhinged hyperbole, and it was.</p>
<p>In reality, the consequences are unlikely to be nearly so dramatic.  The status
quo has a very high amount of inertia, and probably the “Verified” badge will
remain the only visible difference, except for a few repo-specific esoteric
workflows, like pushing trust verification into offline or sandboxed build
systems.  I do still think that there is <em>some</em> potential for nefariousness
around the “unknown and unlimited” dimension of any future plans that might
rely on verifying signed commits, but any flaws are likely to be subtle attack
chains and not anything flashy and obvious.</p>
<p>But I think that one of the biggest problems in information security is a lack
of <a href="https://owasp.org/www-community/Threat_Modeling">threat modeling</a>.  We
encrypt things, we sign things, we institute <a href="https://cryptosmith.com/password-sanity/exp-harmful/">rotation
policies</a> and
<a href="https://neal.fun/password-game/">elaborate</a> useless
<a href="https://xkcd.com/936/">rules</a> for passwords, because we are looking for a
“best practice” that is going to save us from having to think about what our
actual security problems are.</p>
<p>I think the actual harm of signing git commits is to perpetuate an engineering
culture of unquestioningly cargo-culting sophisticated and complex tools like
cryptographic signatures into new contexts where they have no use.</p>
<p>Just from a baseline utilitarian philosophical perspective, for a given action
A, all else being equal, it’s always better <em>not</em> to do A, because taking an
action always has <em>some</em> non-zero opportunity cost even if it is just the time
taken to do it.  Epsilon cost and zero benefit is still a net harm.  This is
even more true in the context of a complex system.  Any action taken in
response to a rule in a system is going to interact with all the other rules in
that system.  You have to pay complexity-rent on every new rule.  So an
apparently-useless embellishment like signing commits can have potentially
far-reaching consequences in the future.</p>
<p>Git commit signing itself is not particularly consequential.  I have probably
spent more time writing this blog post than the sum total of all the time
wasted by all programmers configuring their git clients to add useless
signatures; even the relatively modest readership of this blog will likely
transfer more data reading this post than all those signatures will take to
transmit to the various git clients that will read them.  If I just convince
you not to sign your commits, I don’t think I’m coming out ahead in the
<a href="https://en.wikipedia.org/wiki/Felicific_calculus">felicific calculus</a> here.</p>
<p>What I am actually trying to point out here is that it is useful to <em>carefully
consider how to avoid adding junk complexity to your systems</em>.  One area where
junk tends to leak in to designs and to cultures particularly easily is in
intimidating subjects like trust and safety, where it is easy to get anxious
and convince ourselves that piling on more <em>stuff</em> is safer than leaving things
simple.</p>
<p>If I can help you avoid adding even a little bit of unnecessary complexity, I
think it will have been well worth the cost of the writing, and the reading.</p>
<h2 id="acknowledgments">Acknowledgments</h2>
<p class="update-note">Thank you to <a href="https://www.patreon.com/creatorglyph">my patrons</a> who are
supporting my writing on this blog.  If you like what you’ve read here and
you’d like to read more of it, or you’d like to support my <a href="https://github.com/glyph/">various open-source
endeavors</a>, you can <a href="https://www.patreon.com/join/8655595">support me on Patreon as
well</a>!  I am also <a href="mailto:consulting@glyph.im">available for
consulting work</a> if you think your organization
could benefit from expertise on topics such as “What <em>else</em> should I <em>not</em> apply a cryptographic signature to?”.</p>