public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* Push Certificates: Privacy Concerns Regarding the "pushee" Header
@ 2026-02-15 21:58 Lorenz Leutgeb
  2026-02-17 19:42 ` Junio C Hamano
  0 siblings, 1 reply; 5+ messages in thread
From: Lorenz Leutgeb @ 2026-02-15 21:58 UTC (permalink / raw)
  To: git

Dear recipients,

I am working on an application built on top of Git, which wants to 
ingest push certificates in order to keep (something similar to) a 
transparency log (see 
<https://people.kernel.org/monsieuricon/signed-git-pushes>).

For reasons that I will only go into detail upon request, in the context 
of the application, logical "pushes to the network" are split into two 
parts: The *first part* is always local (from one repository in one 
directory on the filesystem, to another).  Bundled with the application 
is a daemon.  The daemon process then takes over the *second part* of 
the push, for further propagation of over the network in a peer-to-peer 
manner.  However, one desires the *first* part already to be certified.

This would result in push certificates of the following form (hashes 
abbreviated, signature omitted):

	certificate version 0.1
	pusher SHA256:xX6bp…T0  1771188983 +0100
	pushee /home/lorenz/.example/storage/foo
	nonce 1771188983-345389c
	
	0000000 ccae4e0 refs/heads/main

As you can see, this push certificate leaks a path on the application 
users' filesystem.  Here it is quite obviously my home directory, but 
actually the "storage path", is user-configurable at the application 
level, and considered private.

I do realize that the leak is in part due to the weird application 
architecture.  Who would have guessed that I want to certify a push *on 
my own filesystem*?  I am convinced the main motivation for this feature 
were pushes (directly) over the network.  However, I also believe that 
it could be of more general interest to allow the Git user to control 
which/whether the pushee header is emitted.

Handling of the pushee header was introduced to `send-pack.c` in 
9be89160e7382a88e56a02bcf38f4694dd6542d6, over 11 years ago, and was not 
touched since. Remarking on the current implementation, I do realize 
that `transport_anonymize_url` is used to sanitize, in order not to leak 
usernames, passwords, etc., which is appreciated.  However, file paths 
are not removed.  This makes a great deal of sense as the same function 
is used in other places where indeed the path on the filesystem is 
expected.  I think the current implementation is sensible.  However, 
allowing the Git user to ultimately control which URL is used allows for 
the greatest flexibility.

Note that the receiving end might inspect the push certificate in their 
`pre-receive` handler, and reject to receive if the pushee is malformed.

I would like to provide a patch that adds an option `git send-pack`, 
which allows the Git user to specify the pushee or indicate that the 
header should not be emitted.  My proposal is as follows.  Because of 
the connection to signed pushes, and therefore to the the already 
existing `--[no-]-signed=…` option, I would add 
`--signed-pushee=<string>` as well as `--no-signed-pushee`.

One edge case that I would like to get clarification on is how the empty 
string should be handled.  I would consider `--signed-pushee=""` 
invalid, making it impossible to specify the empty string as pushee, 
erroring out and potentially hinting at `--no-signed-pushee`.

Before I do so, I would like to ask for your feedback.  Would you accept 
such patch if implemented cleanly?  What do you think, how should the 
option be named and interpreted?  Further, if this option is added to 
`git send-pack`, one natural question is whether `git push` should also 
"inherit" it, as is the case with `--signed`?

Kind regards,
Lorenz Leutgeb

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Push Certificates: Privacy Concerns Regarding the "pushee" Header
  2026-02-15 21:58 Push Certificates: Privacy Concerns Regarding the "pushee" Header Lorenz Leutgeb
@ 2026-02-17 19:42 ` Junio C Hamano
  2026-02-17 20:31   ` Lorenz Leutgeb
  0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2026-02-17 19:42 UTC (permalink / raw)
  To: Lorenz Leutgeb; +Cc: git

Lorenz Leutgeb <lorenz.leutgeb@posteo.eu> writes:

> This would result in push certificates of the following form (hashes 
> abbreviated, signature omitted):
>
> 	certificate version 0.1
> 	pusher SHA256:xX6bp…T0  1771188983 +0100
> 	pushee /home/lorenz/.example/storage/foo
> 	nonce 1771188983-345389c
> 	
> 	0000000 ccae4e0 refs/heads/main
>
> As you can see, this push certificate leaks a path on the application 
> users' filesystem.  Here it is quite obviously my home directory, but 
> actually the "storage path", is user-configurable at the application 
> level, and considered private.

Stepping back a bit, the most important question is what the signer
is trying to certify, isn't it?

"At this point in time, I am pushing to update these refs because I
want these objects to be sitting at the tip of them in the
repository listed as pushee" is the statement the pusher is
certifying.  The auditors can point at the certificate that the
hosting site having the object ccae4e0 is not because insiders of
the hosting site rewounded the tip to point at a stale object that
the pusher did not want to see at the ref, but with that signature,
this pusher expressed their desire to have it at the tip of the main
branch.

Making it possible to lift that certification and reusing it for a
push to different repository smells like opening a door for replay
attacks, doesn't it?  The pusher did not say anything about updating
these refs in other repositories that are different from the pushee
repository.

I am not sure what the implication of optionally allowing the pusher
to sign a certificate that lacks "pushee" is.  Would that be a
reasonable way to say "at this point in time, I want this object to
be at the tip of main branch in _any_ and _all_ clones of this
project anywhere in the world?"  Is that the kind of statement the
pusher would want to make in the first place?  Would such a
statement even make sense?  When you are dealing with two related
repositories of a project (e.g., one may be the main development
repository, the other being the repository for the maintenance
track), I may fully stand behind putting this commit at the tip of
'main' on the development track, but that no way means I would be OK
to put the same commit at the tip of 'main' on the maintenance
track.  Not naming "pushee" does not sound like it would fly well.

What implication does it have to allow the pusher to sign a
certificate that points at a "pushee" that is different from the
repository the signer directly pushed into?  You give the history
together with a signed "I want the object ccae4e0 at the tip of the
main branch" statement to your middleman, and your middleman does
the actual update and forwards your certificate.  I offhand do not
see a huge security problem in that arrangement.  Anybody can check
the certificate and the resulting history and verify the chain of
hashes to the same degree as you would trust SHA-1 (or SHA-256) for
the object integrity and GPG (or whatever you used to sign the push
certificate) for the certificate integrity.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Push Certificates: Privacy Concerns Regarding the "pushee" Header
  2026-02-17 19:42 ` Junio C Hamano
@ 2026-02-17 20:31   ` Lorenz Leutgeb
  2026-02-18  6:00     ` Junio C Hamano
  0 siblings, 1 reply; 5+ messages in thread
From: Lorenz Leutgeb @ 2026-02-17 20:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

You point out that a push certificate without a pushee is questionable 
(to say the least) and opens the door to replay attacks.  I understand 
and agree.  Thank you.

So let me cross out the option `--no-signed-pushee` and empty values for 
`--signed-pushee=<url>` from my previous proposal, and let us continue 
our discussion under the assumption that the pushee header is always set 
to a URL(-like) string.

On 2026-02-17 11:42-0800, Junio C Hamano wrote:
 > What implication does it have to allow the pusher to sign a
 > certificate that points at a "pushee" that is different from the
 > repository the signer directly pushed into?  [...]  I offhand do not
 > see a huge security problem in that arrangement.  Anybody can check
 > the certificate and the resulting history and verify the chain of
 > hashes to the same degree as you would trust SHA-1 (or SHA-256) for
 > the object integrity and GPG (or whatever you used to sign the push
 > certificate) for the certificate integrity.

Exactly.  And this is very much the situation I am in.  The middleman is 
actually the daemon that comes bundled with the application, which wants 
share the push certificate over the network.  The pusher only has to 
trust the middleman insofar as the middleman will actually forward the 
push to its best ability.  In my application, distribution of the 
certificates and syncing of objects is the main purpose of the daemon. 
But I digress...

Let me zoom in a bit more why I want to override the pushee: The way the 
application works is by means of a remote helper. That is, the user 
executes `git push example://foo ccae4e0:main`.  This is makes sense in 
the context of the application, as the user wants to carry out a logical 
"push to the network", and "foo" is an identifier that also has a 
well-defined meaning within the context of the application.  As you 
know, this will lead to execution of `git-remote-example`.  The remote 
helper is where the hand-off between "plain Git" and the application 
happens.  The remote helper knows that the push will be split in two 
parts.  It knows that it must carry out the first part immediately, 
which is a push to `home/lorenz.example/storage/foo`.  The repository at 
that path is the local view of the repository `example://foo` on the 
users filesystem.  This local repository then gets updated in the 
background by the daemon, and the daemon also notifies other peers on 
the network about the push.  This is the part where it must act as the 
middleman.

Now, in the context of the application, the global identifier of the 
repository across the network, and thus the pushee that I would like to 
see, is `example://foo`.  The path `home/lorenz.example/storage/foo` is 
merely a local name for it, like a cached copy if you will.

Perhaps this even raises a question on how remote helpers interact with 
signed pushes:  If Git allows to register URLs via remote helpers, how 
should remote helpers that do not have the "connect" capability control 
the pushee URL, which they likely are concerned with?

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Push Certificates: Privacy Concerns Regarding the "pushee" Header
  2026-02-17 20:31   ` Lorenz Leutgeb
@ 2026-02-18  6:00     ` Junio C Hamano
  2026-02-18  9:56       ` Lorenz Leutgeb
  0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2026-02-18  6:00 UTC (permalink / raw)
  To: Lorenz Leutgeb; +Cc: git

Lorenz Leutgeb <lorenz.leutgeb@posteo.eu> writes:

> Now, in the context of the application, the global identifier of the 
> repository across the network, and thus the pushee that I would like to 
> see, is `example://foo`.  The path `home/lorenz.example/storage/foo` is 
> merely a local name for it, like a cached copy if you will.

"The repo appears as X to me, but it is known as Y to others" is an
issue that already exists.  "git pull" records from which repository
the changes were merged but it uses the repository from the point of
the view of the user who ran "git pull", for example.  While one of
my public repositories are known as https://github.com/gitster/git",
the URL I use to push there may be "git@github.com:gitster/git.git",
so if they were recording push certificates, the latter would be the
pushee in them, but that is not a URL random people can normally use
to clone from.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Push Certificates: Privacy Concerns Regarding the "pushee" Header
  2026-02-18  6:00     ` Junio C Hamano
@ 2026-02-18  9:56       ` Lorenz Leutgeb
  0 siblings, 0 replies; 5+ messages in thread
From: Lorenz Leutgeb @ 2026-02-18  9:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

So, imagine a world where push certificates are more "end-to-end". 
Think transparency log meets Git (see https://transparency.dev/ for more 
context).  Not only `git push --signed`, but also `git pull --signed` 
exists.  The remote being fetched from must provide evidence for the 
puller verify the range being pulled, e.g. 0000000..ccae4e0.  It does so 
by sending along the blob that contains the corresponding push 
certificates[^1].

In this bright future, where any puller is an auditor, you would run 
into an issue if you want your repository to be pulled from different 
places (pullees? fetchees?).  However, there is a way out: Let the 
pusher specify with which locations they are happy to have their push 
end up at.  In your case, since you are happy for others to pull from 
https://github.com/gitster/git, you add to your push certificate:

	certificate version 0.1
	pusher SHA256:xX6bp…T0  1771188983 +0100
	pushee https://example.com/repo.git
	pushee git@github.com:gitster/git.git
	nonce 1771188983-345389c

	0000000 ccae4e0 refs/heads/main

I will also give you the counter arguments:  Firstly, with repositories 
that have many mirrors, the number of headers might become 
problematically large.  One remedy would be to allow configuration of 
aliases on the side of the puller/verifier.  One could accept all values 
of `remote.<name>.url` as valid.  One could introduce 
`remote.<name>.alias`.  Secondly, for repositories with exactly one 
canonical location, no configuration would be necessary and the value 
being used today would be correct.

---

[1]: In general, it would have to send multiple push certificates. 
Firstly because there might be multiple refs being pulled over multiple 
ranges.  Secondly because that range might have been established over 
multiple pushes.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-02-18  9:56 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-15 21:58 Push Certificates: Privacy Concerns Regarding the "pushee" Header Lorenz Leutgeb
2026-02-17 19:42 ` Junio C Hamano
2026-02-17 20:31   ` Lorenz Leutgeb
2026-02-18  6:00     ` Junio C Hamano
2026-02-18  9:56       ` Lorenz Leutgeb

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox