0.3.30 stops zit sending a password over plain http:// to another host,
and stops a redirect from carrying one to a different host.
When a server gates reads behind HTTP Basic, zit answers the challenge with
the password it was given. It did that whatever the URL's scheme. Basic is
the password base64-encoded, which is an encoding, not protection; over
http:// it crossed the network readable by anyone on the path, and because
a clone saves its URL as the checkout's remote, every later pull and push
sent it again. Against a test server on a LAN address, 0.3.29 sent
Authorization: Basic YWxpY2U6czNjcmV0 — alice:s3cret — in the clear.
0.3.30, given the same command, sent nothing and said:
zit clone: refusing to send credentials over unencrypted http://…; use https://…
Where the check sits matters. It runs when the challenge arrives, not when a
password is supplied, because at that point nobody knows whether one will be
needed. A public repository cloned over http with --user is never
challenged, and still works. Loopback is exempt — localhost, 127.0.0.0/8,
::1 — because plaintext there never leaves the machine, and that is how a
forge talks to its own zit and how the tests run. A name that only looks like
loopback, such as 127.evil.example, is not.
The report asked about http. Tracing it turned up the same leak by a second
route. Once a challenge was answered, the password rode on every later
request in the exchange, and a followed redirect rewrote the target to
wherever Location pointed. Any server could forward the password to a host
of its choosing by answering 302. A redirect that changes host now drops
the credential for the rest of the exchange, which is what curl and browsers
have long done. A redirect from https down to http was already refused,
credential or not.
Plain http to another host now also prints one line on stderr, from every verb that syncs. It is not only about passwords: a private repository's content crosses readable too, whatever authenticates it.