Hacker News new | ask | show | jobs
by 0xbadcafebee 1345 days ago
If you're still using SSH to access your GitHub repos, please consider using HTTPS access tokens instead. The security is much more granular, they're easier to revoke and rotate, easier to generate and use safely, they work over HTTP proxies, you can specify a different user in the URL (https://myuser@github.com/....) allowing for easier use of multiple accounts, and of course, you can use them for the GitHub API too. Security-wise, most people don't use SSH securely and may fall victim to MITM.
4 comments

I agree this seems to be the currently in vogue practice, but I am confused by it!

SSH keys are public-private key pairs, where the private portion can live in a hardware token or software agent that only signs individual challenges and never exposes the private key. (It's possible in theory, although admittedly not common, for the user agent to limit approvals to a particular repository using the techniques in https://github.com/StanfordSNR/guardian-agent . It would be nicer if GitHub would enforce granular permissions on SSH keys!)

If I understand right, HTTP access tokens are... bearer tokens where I have to handle the plaintext (so I can transmit it to GitHub) every time I want to use it? I agree it's nice to have GitHub enforcing more granular permissions, but even so the trade really doesn't seem worth it. I may not be getting something here.

The main reason to use HTTP access tokens is just for the more flexible user-facing functionality. Security-wise, the issue is (to me) not totally clear-cut.

Yes, with HTTP access tokens, you do have to transmit the plaintext each time. (OIDC would be much better, but The Industry Powers That Be decided that non-interactive networked clients had no need to benefit from federated identity and temporary tokens) But the attack vectors (broadly) are 1. at-rest storage (credential managers are good enough, I think) and 2. HTTPS MITM. If the attacker can somehow manage an HTTPS MITM, they get the plaintext token, and can then attack that account offline for as long as the token is valid. It's definitely not easy to HTTPS MITM, but the protocol is much larger and more complicated than SSH, so vulnerabilities are more likely. State actors can do it trivially, of course (though this wouldn't be the case if we hadn't thrown HTTP Client Certificates out with the bathwater!!).

SSH private keys are technically superior, because like you say, the private key can be unexposed (if you use a hardware token etc). This means that the attack vector left is SSH MITM. If the attacker can pull this off, they can only attack when you are connecting, because they don't have your private key to perform arbitrary offline attacks. But how likely is MITM? Well, when was the last time you verified the host keys of GitHub.com? How did you verify it? Are you absolutely sure it was correct, and the host keys transmitted during your initial SSH connection weren't a MITM? Even if they were correct, can the attacker still MITM you? Well, what if they disconnect each of your connection attempts, until you try from a different user/computer, or just remove or disable your host key cache? They might re-try the attack then, and hope you don't verify the host keys this time. All this assuming you didn't globally remove the host key checks in your SSH config (for example, if you connect to a lot of work hosts and you got annoyed by host key verification so you disabled it? maybe some automation script in a ci/cd process is doing this?)

Ultimately the decision of which to use rests with you. Personally, I already rely on HTTPS for most of my security on the internet, so I might as well rely on it for my code, and gain some extra functionality/convenience. But I do wish the HTTPS method used OIDC, and the SSH method had more fine-grained controls.

> This means that the attack vector left is SSH MITM. If the attacker can pull this off, they can only attack when you are connecting, because they don't have your private key to perform arbitrary offline attacks.

Not only that, but that MITM attack could only present modified data to your client; even a successful MITM attacker still cannot authenticate to github as if it were you. The authentication is symmetric, each side authenticates the other.

(A very simplified explanation, the real protocol is a bit more complex: each side combines its private key with the other side's public key, and due to some math the results are identical on both sides; the resulting number is used as a cryptographic key in the protocol. Since the attacker doesn't have access to your private key, it cannot combine it with github's real public key to obtain the cryptographic key github expects. It cannot "pass through" to you the real github public key, because then it wouldn't know the corresponding private key which it needs to do the MITM.)

Thanks for this analysis! To me the risk of bearer tokens seems bigger than just "at-rest storage"... My experience is that the security perimeter effectively ends up including every script or other piece of client software that wants to run Git or has access to the channel where the bearer token is entered. (How would you enforce that your credentials manager will only give the bearer token to some particular trusted HTTPS client software that is talking only to GitHub...?) You have to worry about the token getting logged in a debugging log, etc. And, when I give somebody an account that's authenticated with a bearer token, I'm always nervous maybe their hygiene is not so great and they'll end up accidentally pushing the token somewhere publicly visible. AFAIK you don't really have this attack surface when the credential is a private key stored in a hardware token or, at worst, a software agent that only keeps the decrypted version in private memory and only agrees to sign individual challenges.

On the question of HTTPS MITM vs. SSH MITM, I agree that the HTTPS PKI makes a MITM less likely than the (prevalent) use of TOFU for SSH authentication, but I don't think the risks (or what "MITM" means) are the same. If there's an HTTPS MITM when client authentication is via bearer token, the intermediary can steal the client's token and masquerade as them forever. If there's an SSH MITM when client authentication is via public key, then the intermediary can give bogus data to the client and can get data from the client, but to the best of my knowledge I don't think a true application-layer "MITM" is possible (barring downgrade attacks) -- the intermediary can't spin around and authenticate to GitHub, pretending to be the client. The session ID is part of the authentication scheme.

> How would you enforce that your credentials manager will only give the bearer token to some particular trusted HTTPS client software that is talking only to GitHub...?

I mean, if a client has enough privilege to be able to connect to my credentials manager in the first place, it's already game over. Whatever can execute that client can probably execute arbitrary code, and then use a privilege escalation vuln, so they can do whatever they want on the machine.

> You have to worry about the token getting logged in a debugging log, etc. And, when I give somebody an account that's authenticated with a bearer token, I'm always nervous maybe their hygiene is not so great and they'll end up accidentally pushing the token somewhere publicly visible

Yes, completely valid concern. Not that people don't already push their private SSH keys onto GitHub repos or Jira tickets/email, but that's a lot easier to detect and prevent. But on balance, the bearer is like a password; if the user has any other passwords you care about, you already have to deal with these concerns. The bearer is therefore a "slightly better" password, because you can set an expiration date, limit the scopes, rotate it easier, it's not reused on multiple sites, etc. MFA helps but of course is not a panacea.

And yes you're right with your second paragraph too. (I tried to mention it in my earlier comment but it wasn't clear) I do think that public-key crypto is much better security, but that it's harder for the user to get right, and people have underestimated the feasibility of SSH attacks. I think we're just lucky that there are much easier attacks than MITM so we haven't seen a rash of them. It's much easier to inject malware via a browser, or find an open ACL or network service, etc.

But how do you manage them in practice? At least with ssh everything is in one place in my .ssh folder. I suppose I could create a .tokens folder or somesuch.
Git has a lot of options for managing credentials ( https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage https://git-scm.com/docs/gitcredentials https://docs.github.com/en/authentication/keeping-your-accou... ). For MFA: https://github.com/GitCredentialManager/git-credential-manag...

The simplest thing is to create a ~/.netrc file:

  machine foobar.atlassian.net login myuser1@mycompany.com password isudfiusldifuslkjhdflksjhdf
  machine bitbucket.org        login myotheruser           password kjsdoihohuaoivhdifhuvoiadhf
  machine github.com           login companyuser1          password ghp_oisjdofhowuefoiusiofus
  machine github.com           login personaluser2         password ghp_jf9huiehuwfsouyewuhifuh
  machine circleci.com         login myotheruser           password lkjdhiufwhu8ef7yw8yoefhozheofuhouha4lhlWiur
Clone a repository like git clone https://companyuser1@github.com/foo/bar.git and Git will load the right login automatically.
The idea of having sensitive and important credentials sitting in plaintext in a file in the root of my home directory gives me heebie-jeebies.
Do you typically grant access to your home directory? The .ssh folder is also in your home.
On most posix systems everything running as your UID has access to everything in your home directory.

I have no private key material or credentials in my .ssh folder (other than usernames and hostnames). All of my SSH private keys are stored in hardware.

Don't put valuable password in .netrc:

https://jwilk.net/blog/20131104-netrc-security

Interesting, thanks. Can they at least be password protected like ssh private keys? If so is there any equivalent to ssh-agent?
Yes; if you dig into the links I left, Git can use a variety of credential managers to protect them.

Speaking of ssh key passwords: until OpenSSH 7.8 (2018-08-24), private keys using the PEM format were vulnerable to brute-force password cracking. You had to specify the -o option to use the more secure OpenSSH-format keys. Today the -o option is the default (and thus gone), but you might want to rotate your keys if they're from before September 2018.

Oh, thanks for the heads up I did not know that about PEM keys!
Probably ~/.netrc can handle per repository tokens
My SSH key is on hardware token. How HTTPS tokens would be more secure ?

Other question would be I guess "why we don't just use HTTPS client cert auth instead of silly tokens" ?

> Security-wise, most people don't use SSH securely and may fall victim to MITM.

The whole things sounds like "okay so some of you are incompetent, let's put some measures that annoy the ones that are"

> The whole things sounds like "okay so some of you are incompetent, let's put some measures that annoy the ones that are"

The proper measure is to secure SSH keys. For me it seems crazy that I must Google github host key fingerprints. SSH must retrieve those keys over some standard URL like https://github.com/.well-known/ssh rather than asking user a question that he'll ignore.

My guess would be that HTTPS checks the certificate, but SSH does not (does it?). First time you connect to a server with SSH, you have to accept a fingerprint. And once in a while it complains for some reason, and the typical move is to erase the file that stores those fingerprints, right?

Well this is not the right way to do it (vulnerable to MITM), but that's how I see most people use it.

Your hardware key mostly ensures that nobody steals your private key, but it cannot magically authenticate the remote server (which is what you need against MITM).

> [...] And once in a while it complains for some reason, and the typical move is to erase the file that stores those fingerprints, right? Well this is not the right way to do it (vulnerable to MITM), but that's how I see most people use it.

Yes, that's vulnerable to MITM, but with a caveat: that MITM could impersonate github to your machine, but it could not impersonate your machine to github. That is, it could present modified data to your git client, but it would have no access to your github account. With HTTPS without client certificates, once someone successfully impersonates github to your git client, they have the same access to github that your git client has. (HTTPS with client certificates would be as secure as using SSH keys, since the MITM could not impersonate the client to the github server.)

You mean HTTPS access tokens that are sent in the clear and accessible to any proxy server in the middle?

They are a good replacement of password based auth, and yes they are new. But that doesn't mean they are better than ssh keys.

Each time you authenticate to github you send that token in the clear (inside the TLS session, but there it's in the clear). This means that any proxy server on the way, anyone with access to github's infrastructure who gets to read logs of traffic, etc. can get a means to impersonate you. That's a major problem with this authentication protocol, and a step back from signature based ed25519 keys that ssh uses. There are HTTP auth schemes 10 times better than that (TLS-SRP, etc). I'm not saying that HTTPS access tokens were a bad addition, they are better than doing the same thing you do with these tokens but with passwords. But they are worse than ssh keys.

FTR you can also specify different users with ssh keys, by having custom hosts in ~/.ssh/config that point to different identity files.

Typically proxy servers don’t have access to the contents within an ssl session unless they are MITM the whole transaction. And at that point they could just MITM passwords and everything else sent through the session.

They are worse than ssh keys but I think are still acceptable as I don’t use any proxies that MITM my ssl sessions and I’d be able to detect if someone tried (assuming the root CA don’t go crazy and start allowing it).

Depends on the type of proxy you are using, but yes, I should have pointed that out that many proxies do not access encrypted contents. Doesn't change the fact that such traffic is way more dangerous if the tokens are in the clear.

For ssh you also have multiple types of proxying, some which send your keys to the proxy server (very bad), others which base on encrypted tunnels and don't do that. HTTPS is I think similar to that.

> at that point they could just MITM passwords and everything else sent through the session.

For proxy servers you are right. but any read only vulnerability can be turned into a write vulnerability. e.g. that attacker who has read-only access to logs of github traffic can turn use that for an attack where they push commits, etc.

> Doesn't change the fact that such traffic is way more dangerous if the tokens are in the clear.

It’s very important as tokens are not in the clear, they are encrypted in the ssl session. They are not visible to anyone other than the client initiating the session and the server authenticate by the server cert.