I learned about this when I encountered a server with an aggressive fail2ban that wouldn't let me log in because I had too many ssh keys. It apparently counted every wrong key as an auth attempt, so it blocked me before my ssh client tried the right key. Since then I've used
IdentitiesOnly=yes
Before SSH-ing into untrusted hosts, make sure you understand what SSH agent-forwarding and X11-forwarding are.
Also, maybe don't trust the SSH config defaults on whatever host you're on at the moment. You can explicitly set defaults yourself in your `~/.ssh/config` or similar.
> ... make sure you understand ... SSH agent-forwarding ...
And how to configure your ssh agent to confirm with you on each use. See `-c` in `ssh-add(1)`, and make sure you're using an agent that supports it (GNOME's Seahorse doesn't, gpg-agent does, macOS's ssh-agent doesn't make can be made to via some AppleScript).
Because my ssh agent confirms each key use, I enable agent forwarding on every ssh connection without fear.
This doesn't really turn off the behavior. I think you're looking for IdentitiesOnly and IdentityFile, which lets you tell your client to send only the right key per host, rather than sending all of them until it gets a hit.
This seems like bad advice, password auth is less secure than key auth^. And many servers don't accept password-based auth at all.
^password is sent to the server directly; passwords are generally weak and easy to brute force. Pubkeys without a passphrase _can_ be stolen from the local machine, but if an attacker has access to your local machine, you are probably SOL anyway.
edit: as several people have pointed out, this config option does not completely prevent pubkey auth being used (i.e. if configured or overriden on the command line). But if you only use that config by itself, it will disable pubkey authentication for every host.
That's a petty interpretation, it's a big leap reading "don't send your unique identity to strange servers by default" as "never use private keys, always use passwords instead."
Nothing about that config snippet precludes using private keys for known servers.
Excellent way to also do ssh-agent less key site binding for those who are ssh adding a new key. I would always rather manage a key per host than manage it inside of the ever forgetful agent.
I have been circumventing this issue purely by accident it seems.
I have always had ssh-keygen write out the keys to a custom path like ~/.ssh/keys/$service/$key{,.pub} and configured each host's "identity" manually with the ssh config file.
I never did this for security purposes or anything, I just thought it made more sense than the default behavior.
Seems questionable on Github's part to have .keys public by default, why not allow people to opt in ex: keybase.io equivalent?
Yes I get that its not sensitive information, but as this demo demonstrates it can fingerprint people who might not be unaware re: this quirk of SSH's coupled with this part of the Github API.
So users can add other users' SSH keys (sourced from GitLab, ....) to their GitHub profile, essentially depriving the actual key owner from using GitHub
> The first is that the SSH protocol provides to the server all public keys the client is willing to produce signatures for, which by default are all the public keys in your ssh-agent and in your ~/.ssh/id_*.
Not sure about other systems, but this isn't the default on Debian. I, at least, start every day by running `ssh-add ~/.ssh/id_whatever_service_I_want_to_auth_to`, because it doesn't do it by itself, and I was too lazy to configure it. For the same reason, the party trick didn't work here, since I haven't had to commit anything today yet ...
I think it's pretty clever, and demonstrates something very powerful about GitHub's position as de facto global code repository: you can get a strong cryptographic identity for (almost) anyone on the service, which you can then sign/encrypt to, verify for, etc.
age (another tool of Filippo's) leverages this to make encrypting to any GitHub user easy[1].
In the end, it isn't that useful. I only routinely sign digitally to deal with the (Spanish) government, and they provide their own certificates and software to do that.
As in if someone pings your server and you get a key that matches their GitHub is it really that person's key or could they be doing it without having the corresponding private key?
Since Github SSH keys are public for every account, wouldn't anyone be able to impersonate you if they know your username, if you haven't set up a passphrase? If yes, it is surprising to me that Github does not make it abundantly clear when you add a key. Until now, I thought the only way an attacker can get my keys is if they break into my computer.
Edit: totally disregard this, absolute brain fog moment
You actually were wrong to disregard this entirely although of course having a public key doesn't allow somebody to trick GitHub into thinking they know your private key -- the trick this whoami server is doing could indeed be fooled that way.
When you call GitHub, after the server proves who it is to the client we get to user authentication, the client says e.g. "I'm tialaramex, and I can prove it, I know tialaramex's private key corresponding to public key 123456, and also private key corresponding to public key 987654". The server looks at the claimed user identity and keys and it can decide it wants to see that proof, for a real SSH server that'll be because it knows those keys are allowed to authenticate for that user (e.g. on a default Linux they're in the user's .ssh/authorized_keys file)
But whoami isn't a real SSH server, it's just looking at the claims, and anybody can make such claims.
If you get my GitHub public key, and you tell whoami "Yeah, I can prove I'm tialaramex, I know the corresponding private key for this public key" the whoami service doesn't actually check you can do that, it just says OK, I guess you're tialaramex.
A more nefarious thing is possible, which I'll mention but it isn't what whoami does. If you run a broadly used SSH server which actually authenticates users, you could scan those public keys against data sets like the one from GitHub, and correlate your users. So e.g. you can see that "Kittens4Ever" on your service is using the same key for that service as "DogsRuleCatsDrool" on GitHub and now you know those are in some sense the "same person". This can't be spoofed because you're actually checking those private key signature proofs, which whoami does not do.
This key correlation is why Security Keys (say a modern Yubikey, or cheaper alternative tokens) mint a brand new random private key for every enrolment. When I enrol the exact same token at Live.com, at GitHub, and at Facebook, the device mints three separate keys which can't be externally correlated, even though I can sign in seamlessly in all three places. So I could authenticate to the Live.com account "HackerNewsNoob" and the GitHub user "DanGFanClub" which says I've followed the site for many years - using literally the same Yubikey to do it - and even though Microsoft owns both services I'd have to give away the connection by some other means (e.g. linking them both on my HN about box, just using my public IP addresses not Tor or VPNs) or there's no practical way they'd be able to connect the dots.
The existence of lesser-known things like this, combined with the defaults of SSH clients, and the rabid promotion of keypair auth instead of passwords, really makes one wonder whether there is some ulterior motive to make it easier to deanonymise and thus track users...
$ ssh whoami.filippo.io
+---------------------------------------------------------------------+
| |
| _o/ Hello! |
| |
| |
| Did you know that ssh sends all your public keys to any server |
| it tries to authenticate to? You can see yours echoed below. |
| |
| We tried to use them to lookup your GitHub account, |
| but got no match :( |
| |
| -- Filippo (https://filippo.io) |
| |
| |
| P.S. The source of this server is at |
| https://github.com/FiloSottile/whoami.filippo.io |
| |
+---------------------------------------------------------------------+
Of course, this happens because the day I learned about the default behavior of SSH (to send all your keys in hope that one works), I went ahead to disable it to stop remote servers from being able to inspect all my keys. I feel this can be abused in a similar way that sites abuse browser information to fingerprint users. So I put this at the bottom of ~/.ssh/config:
Host *
IdentitiesOnly yes
And then I explicitly indicate what key to use for each server, either with the "-i" argument, or adding entries above the previous lines:
Other commenter mentioned that something similar can be achieved with "PubkeyAuthentication no", but I've been using "IdentitiesOnly yes" for years without issue.
Is it just as OP states in article, where you have to interact with the authentication process to provide a key (assuming no key is associated to host as you explain)?
The downside is that if you use a large range of servers, you will have to configure them to tell SSH what identities to use. This can be cumbersome if you ssh by alias (e.g. 'foo' rather than 'foo.yourcompany.com').
If you only SSH into servers you trust (a sensible practice) then the benefit is marginal.
Note that it does accept wildcards so you can do rules for *.mycompany.com
You can also use placeholders for keys, so for example I have a ssh config like:
Host *.mycompany.com
# Employer specific yubikey stuff
Host *.mydomain.com
IdentityFile ~/.ssh/keys/id_primary
Host *
IdentitiesOnly yes
IdentityFile ~/.ssh/keys/%r@%h # uses ~/.ssh/keys/git@github.com for github for example
I originally started doing this because I would have so many keys that servers would reject me for too many authentication attempts, but it also helps make it easy to use distinct keys for distinct purposes and avoiding fingerprinting like this*
Yes that's exactly what I do. I just left it as an "exercise for the reader" to not make the comment too long :)
This kind of stuff is also useful, for example, for AWS machines:
Host ec2-*.compute-1.amazonaws.com
# KeepAlive of 50 seconds, because AWS times out after 60:
# https://aws.amazon.com/blogs/aws/elb-idle-timeout-control/
ServerAliveInterval 50
The idea of walking up to a lock and saying “here are all of my keys. Do any unlock you?” is kind of weird and backwards.
But I realize, thinking about it, I was doing that all the time at a previous job where I’d just mash my entire wallet against the keycard reader.
Bonus tangent: join me in playing “Payment Roulette” where you mash your wallet against payment terminals and let your credit and debit cards sort out which is going to pay.
A lot of times the machine would say "Use one card only", won't it? I have a Secrid wallet and I was expecting the aluminium to block the RFID reader except for the 1 card I put in the outer leather pocket, but I still need to open the wallet up and present the dangling card in its sleeve to stop multiple signals...
Not quite because there is no public/private key analogy. With a physical key there isn’t much security and you are relying on the copying of the key (if someone takes a photo) not happening. There would need to be some sophistication and anyone that sophisticated wouldn’t need to burglarise you. And it is that fact you rely on for a key. It is almost like a credit card PIN
I’m pretty confident that POS transactions are idempotent. It’s an entire state machine that you’re working through, too, so I doubt it’s capable of performing multiple transactions during the middle of one transaction.
$ ssh whoami.filippo.io
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The ED25519 host key for whoami.filippo.io has changed
I think a lot of people in the comments are missing the fact that public keys are by default intended to be public. This is an explicit and intended property of private-public key cryptography. Your public keys are not a security boundary and setting `IdentitiesOnly` in your SSH configuration does not do anything to strengthen your security posture.
There's no mention of all your public keys being stored on connection, right? It's just comparing public keys it gets sent against what's already public on GitHub
[0] https://man.openbsd.org/ssh_config.5