| ▲ | kazinator 8 hours ago |
| The problem of a SH1 collision happening by coincidence is vanishingly low and theoretical. Nothing else matters. Git hashes are not supposed to be a security mechanism. If your basis for trusting that you have the right checkout is the git hash, in a situation where you have legitimate concern about untrusted parties manipulating remote repositories, then you're simply wrong. |
|
| ▲ | zygentoma 7 hours ago | parent | next [-] |
| Sorry, no. When I check out code from a git repository in a pipeline using a git hash, I expect the code to be exactly what has been reviewed by me under that hash. Everything else would just be a crazy invitation to make supply chain attacks uncircumventable. |
| |
| ▲ | kazinator 7 hours ago | parent [-] | | And so if you don't trust the server that is hosted on or the security of the transport mechanism like TLS/SSL, such that the content may be manipulated by adversaries, you think that git hashes are good enough? Well, what about someone who is fetching the commit from that server for the first time and has nothing to compare the hash against? Oh, that would never be a problem for widely disseminated, popular, open source project, so it doesn't matter. | | |
| ▲ | kstrauser 17 minutes ago | parent | next [-] | | Rumor has it that GitHub has a flat namespace for commits. They don't store "user1/repo1/abcd1234" in one file and "user2/repo2/abcd1234" in another. Both references point to the same commit in a global shared space. If the hashes are truly unique, then that never matters, because the odds are approximately 0.000000000...000 of you and I accidentally generating the same commit. However, if I see that you pushed commit abcd134, and then I can build and push a colliding commit, and the backend doesn't check uniqueness before writes because the odds are infinitesimal that it'd ever matter, than voila, I've updated your repo by writing to my own. Or if first writer wins, and I know that you have a popular non-GitHub repo that you're about to migrate into it, then I could pre-poison the namespace by writing my own version of a commit that I see you already have in Codeberg or Savannah or wherever. I don't swear that this is how GitHub actually works, but I've had knowledgeable friends swear up and down that it is. And honestly, it'd make sense. They could shard storage by the first 4 digits of the hash or something, and that'd be vastly more efficient if all commits were writing to the same space. | |
| ▲ | crote 6 hours ago | parent | prev | next [-] | | Dealing with potentially-hostile hosts is quite common, actually. See for example how most Linux mirrors work, or Subresource Integrity with HTML. Turns out securing a service to transfer a single hash is a lot easier than securing a service to transfer gigabytes of data. Even if I don't fully trust Github, it is still incredibly convenient to be able to upload my code there and then send someone an email telling them to fetch commit `123abc` from some repo link. As long as my email isn't compromised, that should be secure. | |
| ▲ | PunchyHamster 3 hours ago | parent | prev [-] | | I mean, Git commit signing should be used more often... then you can actually trust the person signing, not the distribution method But you still need SHA256 for that |
|
|
|
| ▲ | shakow 8 hours ago | parent | prev | next [-] |
| > Git hashes are not supposed to be a security mechanism Probably a naive question, but why not kill two birds with one stone if it can be done for a reasonable cost? |
| |
| ▲ | kazinator 8 hours ago | parent [-] | | Because you're not killling two birds; you're not killing the security bird with a better content hash. A SHA-256 sum, though very good, only assures you with great confidence that you're looking at the same thing you looked at before, or that someone else is looking at elsewhere. It is not a digital signature, and we don't want digital signatures to serve the role of content hashes. Speaking of signatures, we have support for them in Git; you can use gpg to sign commits, and set it up to be done automatically. Nobody is going to fake your commit such that the fake has the same SH-1 hash and your GPG signature. The worry there is that the key holder (whether the legitimate one, or a malicious party who got a hold of the key) somehow does this: creates a new commit, signed with their key, which somehow has the same SH-1 as an existing signed commit. The git hash includes the GPG signature, so there is a significant layer of difficulty there which is likely harder than faking an unsigned SHA-256 commit. | | |
| ▲ | ramses0 an hour ago | parent | next [-] | | Dude... please bow out gracefully... The attack is I pre-author `Makefile => foo: echo "hello"; bar: echo "world"` along with `Makefile => foo: echo "hello"; bar: rm -rf / ; /* $ELDRITCH_SHA1_SPIRITS_GO_HERE */` that both hash to `ff1234...` I then prepopulate the repo with `echo "hello"`, wait 6-9 months, then submit a commit for `echo "hello" ; echo "world"` and keep (in my back pocket) the alternate implementation that also includes $ELDRITCH_SPIRITS to force a collision and MY predetermined change in functionality. I then have free choice as to whether I serve them "hello world" or "hello && rm -rf", and THAT's the plausible problem to avoid: the ability to "cloak" content anywhere within the repo if you have enough $ELDRITCH_SPIRITS and GPU's. You have _really_ good points, but are woefully confused. The proper answer is (would have been) to include `tree ff12354...` along with `tree-sha256 abc123456789...` for another 20 years along with a `[git.hash_strictness]: default/lazy/strict`, and some oddball `git-rerere` type packfile extension which lets you map `sha1:ff1234... => sha256:abc123456789...` "transparently" rather than the horrific situation you're laying out (correctly!) that forks the ecosystem in to "longhash" and "shorthash" when most repos don't even care in the end. | | |
| ▲ | kazinator an hour ago | parent [-] | | > woefully confused Yes, I didn't understand that the GPG signing just operates on the top level object in the commit and trusts the SHA-1 hashes contained in it. The signing process doesn't recursively traverse the bytes of the commit to pull them into GPG, like you would expect. It's like, imagine you made a "bill of materials" of your project's files consisting of their names and CRC-32 checksums, and then signed this file, and called your project securely signed, LOL. This aspect can be fixed without foisting new hashing scheme into the content tracker. In fact, it must be fixed; users on SHA-1-based repos deserve secure signing. It's really sneaky that the SHA-1 business (not intended to be a security mechanism) was embroiled into the signing implementation; that GPG is demoted to the strength of SHA-1. Was that just to save some cycles? It's certainly faster just to sign the commit object! | | |
| ▲ | singpolyma3 19 minutes ago | parent [-] | | signatures are basically always computed over hashes. The only problem here is that the hashes are not secure. And this is being fixed. |
|
| |
| ▲ | kpcyrd 7 hours ago | parent | prev [-] | | Please educate yourself what a merkle tree is. It's a well understood building block of various security systems, including certificate transparency (which explicitly uses sha256). You refer to PGP signed Git objects, but you also argue: > Git hashes are not supposed to be a security mechanism Guess what the Git PGP signature is signing. | | |
| ▲ | layer8 7 hours ago | parent | next [-] | | This is exactly right. A signature is only worth as much as the hash that it’s signing. And all the usual signature algorithms are signing a hash. | |
| ▲ | kazinator 7 hours ago | parent | prev [-] | | The GPG signature is not signing the git hash, if that's what you mean. The GPG signature signs some kind of hash calculated over the commit, minus the GPG header, which is thereby added. The git hash is then calculated over the whole thing. The git hash is on the outside, and not part of the signing. | | |
| ▲ | orf 4 hours ago | parent | next [-] | | > The GPG signature is not signing the git hash, if that's what you mean. It kind of is - it’s signing the hash of the tree object, which is the actual thing that you’d attack with a hash collision | | |
| ▲ | kazinator 4 hours ago | parent [-] | | I understand that if we sign a commit with the help of some arbitrarily strong hash, it doesn't protect the parent commit(s). The integrity of the SHA-1 hash references to the parent commits is not in question, but the authenticity of those commits themselves. | | |
| ▲ | orf 4 hours ago | parent [-] | | No, not the abstract tree formed by a series of commits. The actual git ‘tree’ object, which is the thing a commit actually points to, referenced by a hash in the commit. That is signed by the GPG signature. |
|
| |
| ▲ | crote 6 hours ago | parent | prev [-] | | That doesn't make a difference: with sha1 a malicious change in content will still result in the same content hash, so the signature will still be valid, and the commit hash will still be the same. | | |
| ▲ | kazinator 6 hours ago | parent [-] | | Only if the GPG signing process stupidly relies on the SHA-1 hash. I.e. if it takes an unsigned commit and signs only its SHA-1 hash and then creates a new commit with GPG headers. If that's how it works, that is massively stupid and can be fixed without forcing SHA-256 as a git hash. Just have the signing calculate its own digest for its own purposes. That digest can be the SHA-256; since the infrastructure is there for it, signing should use SHA-256 regardless of what hash is used by the repository for identifying and linking content. | | |
| ▲ | semiquaver 3 hours ago | parent | next [-] | | > if it takes an unsigned commit and signs only its SHA-1 hash and then creates a new commit with GPG headers
It does indeed. The bytes passed to GPG when constructing a signed commit look something like: tree eebfed94e75e7760540d1485c740902590a00332
parent 04b871796dc0420f8e7561a895b52484b701d51a
author Alice <alice@example.com> 1465981137 +0000
committer Alice <alice@example.com> 1465981137 +0000
Headline
Message
where the contents being signed are entirely represented by the oids of the tree object and parent commit object. This string is very similar to the content that is fed to the hash function to produce a normal git commit object id. | | |
| ▲ | kazinator an hour ago | parent [-] | | Haha, well that is a screw up. The weak tree hash can be attacked, replacing the content that is itself not pulled into GPG. The "bytes passed to GPG" of course get hashed by GPG, using something better than SHA-1. All bytes that comprise the commit should be hashed by GPG, rather than depending on the content referencing hash in the object tracking system. This is something that is possible; it is not a logically deductive necessity that we just scan the topmost object and trust the hashes it contains. | | |
| ▲ | dwohnitmok an hour ago | parent [-] | | > it is not a logically deductive necessity that we just scan the topmost object and trust the hashes it contains. It kind of is. Otherwise the whole idea of signing a commit with a backing git history (rather than just a snapshot of a working directory) collapses. The only guarantee you have that the git history is what is claimed by the cryptographic signature is some sort of Merkle tree structure. Either the original one, or you have to construct a whole new parallel one with a better hash, in which case, as I bring up in a cousin comment, why not just use a better hash in your original one? |
|
| |
| ▲ | dwohnitmok an hour ago | parent | prev [-] | | > If that's how it works, that is massively stupid and can be fixed without forcing SHA-256 as a git hash. I don't think it's massively stupid. Unless you want to re-hash the entire Merkle tree structure to sign your commit, you basically have to trust the hashes in the Merkle tree (or have a separate parallel Merkle tree) at some point in what you sign, which means you do have to trust the SHA-1 hashes. Otherwise even with a cryptographic signature you can always spoof at least the git repo history (e.g. even if you try to directly hash the entire contents of the current commit). Re-hashing the entire Merkle tree structure seems prohibitively expensive to generate (even with a lot of caching) and pretty complicated for e.g. verifying a signature. Or you can do that incrementally, but then you're just generating a whole new parallel Merkle tree structure. Regardless, at the end of the day, you need to trust the integrity of the Merkle tree structure. And you can either do that by trusting the hashes of the current Merkle tree, or you have to completely recreate a new one with more trustworthy hashes, in which case why not just use better hashes in your original tree? |
|
|
|
|
|
|
|
| ▲ | SAI_Peregrinus 5 hours ago | parent | prev [-] |
| > Git hashes are not supposed to be a security mechanism. Commit signing indicates otherwise. |
| |