Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Cert and private key are created in the directory which is served by default, so an attacker can just download and use them in a MitM attack #3

Closed
philippgille opened this issue May 5, 2019 · 3 comments

Comments

@philippgille
Copy link

I'm pretty sure there's a big issue in the current version of serve, but I'm not a security expert so some details in the following description might not be 100% correct.

When running serve -2, serve serves the current working directory, and also saves the generated certificate and private key in the same directory. This means everyone can just download both files and use them in a Man-in-the-Middle attack with the following consequences:

  1. The attacker can decrypt and read all data that's usually encrypted by the TLS connection
  2. The client has no way to notice that he's the victim of a MitM attack because the certificate he sees is the correct one

Usually, while the first part of a MitM attack (routing traffic from a victim via the attacker's computer to the server, leading to seeing all network packets) is easy, the second part (decrypting the packets and having the victim not notice that he's attacked) is very difficult.
Without the certificate and private key the attacker can only generate another self signed certificate. When routing the traffic from the victim via his computer, he can now decrypt the traffic (because the TLS connection is only encrypted between the victim and himself, and a seperate TLS encryption is encrypted between himself and the server), but if the client knows for example the fingerprint of the original server's certificate, he can now tell that the certificate he's shown is different.

So what should be implemented here is two things:

  1. The certificate and private key shouldn't be written to the same directory that's served
  2. There should be an easy way for the server to send a fingerprint of the certificate to a client via a secure channel, so the client has a way to validate that the certificate he's shown is the correct one (maybe just print the fingerprint so the person running the server can then send it via chat app or put it on his website or something)
@rhardih
Copy link
Owner

rhardih commented May 6, 2019

Hi Philipp. Thanks for taking the time to look into this. Your points are valid from a general security standpoint, but there's a couple of reasons I think the severity is considerably less for serve.

Serve is intended for quick localhost file-serving, among the same use case where you'd fire up $ python -m SimpleHTTPServer in the terminal. That means accessing either via localhost:8080 or 127.0.0.1:8080, which in both cases intercedes a DNS lookup, ie. afaik no trafik ever escapes your own device.

No matter if there's a MitM attack or not, the certificate being self-signed and not root valid, will trigger ERR_CERT_AUTHORITY_INVALID and prompt the visitor to manually accept it in any case.

Exchanging a fingerprint out-of-channel, of the original certificate solves this in theory, but how do you propose to do this in practice? Does the user extract the cert from his browser session and manually run a sha256 on the file and then compare, with previously shared fingerprint? Seems like a usability nightmare. Personally, I know I wouldn't bother with that, just to serve a few files for a short while and probably trade ease-of-use with the inherent probability of risk. How do you propose to solve this in a user friendly manner?

Also, this all comes down to if you're on a trusted network as well. Worst case scenario; you're co-working at your local coffee shop, and you decide to serve files over https across the network to someone else. If evil-barista-network-admin has compromised the network, at link level, yes we're in trouble and we can't trust the validity of the files served. No matter if he has access to our self signed certificate or not. As you say, he can just be the unencrypted middle man, with his own certificate, and we'll be none the wiser.

We could put the key and cert in ~/.serve for instance, but due to the above mentioned inherent invalidity of self-signed certificates, it wouldn't really provide any meaningful security, as the client just wouldn't know, whether they're accepting that certificate or a middle-mans.

Perhaps just putting up a big disclaimer whenever using https from -2, warning to expect no security difference from standard http, is the way to go?

@philippgille
Copy link
Author

Thanks for the quick and extensive reply! I know the issue is not going to affect the average user at all, but some of your assumptions are incorrect I think and even if only very few people are ever affected, if it's network related security where a MitM might happen, this could be a worst case scenario for them. And if there's an easy way to mitigate this, it should be done. Let me elaborate:

Serve is intended for quick localhost file-serving

If serve was restricted to localhost file serving there would be no issue. But it's not - you're binding to "0.0.0.0", which means anyone can access the service. Now serve might be intended for localhost file serving, maybe even just for localhost web development, but as soon as people realize they can quickly share this 20 MB PDF with their coworker with starting serve temporarily instead of using email or Slack, which would waste cloud storage space, they're going to do that. And now data is flowing through the LAN.

Also, this all comes down to if you're on a trusted network as well.
[...]
Worst case scenario;
[...]
If evil-barista-network-admin has compromised the network, at link level

That's by far not the only possible malicious actor. There only needs to be a single malicious piece of software on any device in the network (some malware on a coworkers laptop, some malware on your home security camera or other IoT device) and then it can do ARP poisoning, leading to the traffic between a victim and the gateway now going through the device with the malicious software.

[...] we can't trust the validity of the files served. No matter if he has access to our self signed certificate or not. As you say, he can just be the unencrypted middle man, with his own certificate, and we'll be none the wiser.
[...]
but due to the above mentioned inherent invalidity of self-signed certificates, it wouldn't really provide any meaningful security, as the client just wouldn't know, whether they're accepting that certificate or a middle-mans.

This is not correct. Let's take the example above and a coworker serves a directory with a PDF, but this time it's not just any PDF, it includes the mission details for a secret agent. If his coworker knows the fingerprint of the certificate (sent via WhasApp or some other end-to-end encrypted communication channel), he can now easily check if the server he accesses via web browser is the real one. If evil-barista-network-admin or the hacked security camera had created a new certificate, the fingerprints wouldn't match and the coworker would know that someone's trying to MitM him.

The attack vector here is not that the attacker can see and download the PDF, but that he could manipulate it without the client realizing it.

Yes, your average user isn't going to be a secret agent :) .
Yes, this fingerprint check is only possible when both coworkers take these extra security measures, and only few will do it (or even require to do it).

But some might still depend on this.

Does the user extract the cert from his browser session and manually run a sha256 on the file and then compare, with previously shared fingerprint? Seems like a usability nightmare.

Luckily it's not that complicated:

  • Firefox click path: Lock icon in address bar, right-arrow, "more information", "view certificate"
    • That's it - you see the "fingerprints" (SHA1 and SHA256) on that screen
  • Chrome click path: Lock icon in address bar, "Certificate", "Details" tab
    • That's it - when scrolling down you see "Thumbprint" and the value

Exchanging a fingerprint out-of-channel, of the original certificate solves this in theory, but how do you propose to do this in practice?

As mentioned above, I don't think this needs to be included in serve, or I rather even think it shouldn't be included. The secure channel should be a separate one and it should be chosen by the participating parties.

And as also mentioned above: yes that limits this security measure to the few people that take this extra step, but at least they have the possibility to do so.

We could put the key and cert in ~/.serve for instance

Yes that's one possibility. Or don't save the certificates in a file at all.

You see, I wrote the exact same software one year ago, even with exactly the same name (but didn't know about your repository back then), and just recently implemented generating a self signed certificate for HTTPS. I know most Go examples for generating a certificate include writing the certificate and private key to files, but I accidentally found this PR to a repo that contains a Go package for generating self signed certificates: kabukky/httpscerts#1

So this is how I do it:

Maybe you can use parts of that code :)

@rhardih
Copy link
Owner

rhardih commented May 7, 2019

In-memory certs sounds reasonable and I guess it would be necessary to echo the fingerprint on launch with -2, so it can be sent to potential clients.

I'll leave the issue open until I get around to making the change. 👍

rhardih added a commit that referenced this issue Jan 22, 2023
As pointed out in #3, storing the
generated certificate and key in the same directory as the one you're
serving with a file server, makes it possible, by default, to seize
them and do a MITM attack.

This change makes it so certificates are stored in memory by default
instead of on disk.

In some cases it might be nice to not have to accept an exception for a
new certificate in the browser, every time the server is started up and
for that two new flags have been added. --cert-save, --sc and
  --cert-dir, --cd.

These makes it possible to still store the certificate on disk, but
defaults to somewhere outside of the folder being served. (~/.serve)

This fixes #3.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants