How to manage SSL key for self host HTTPS
Asked Answered
K

2

7

I have Windows service that listens for https requests on an end user's machine, is there an accepted way of creating or distributing the private key in this circumstance? Should I be packaging a real key specifically made for localhost requests (e.g. local.mydomain.com) or generating a self signed key and adding a trusted root CA at install time?

If it matters, the service uses Nancy Self Host for handing the requests and it runs on the SYSTEM user. We have a web app running over https that will be making CORS requests to the service, the user will be using it on a standard web browser (>=IE10). Only the machine onto which the service is installed will be making requests to it.

Thanks.

Kandis answered 14/1, 2016 at 14:2 Comment(4)
Can't you just use plain http and listen on localhost (127.0.0.1) only? As the only machine that will make requests is the same?Seena
The site that will be making the calls is served through https, so CORS requests need to be as well.Kandis
@JussiKosunen maybe this link could give you some ideas. timtaubert.de/blog/2014/10/deploying-tls-the-hard-wayYonit
@JussiKosunen why do you need localhost to support HTTPS? Is it because other content is served from e.g. https://some.server.com on the internet and you want user's browser to play nicely?Admissible
A
2

I have 2 options for you, doing the right way and not doing it at all.

The right way

(Warning: it costs a plenty)

Let's say your application is hosted in the cloud, under kosunen.fi. Main portion is served from the cloud at https://www.kosunen.fi.

Buy DNS delegation for this domain. Resolve localhost-a7b3ff.kosunen.fi to either 127.0.0.1 / ::1 or the actual client's local ip address 10.0.0.63 / fe80::xxxx

Buy subCA or get mass certificate purchase agreement, issue certificates (earlier) or get certificates issued (latter) on demand for each localhost-a7b3ff.kosunen.fi. These certificate will emanate from a trusted global CA and therefore are trusted by all browsers. Each certificate is only used by one PC.

Set up CORS/XSRF/etc bits for *.kosunen.fi.

Done.

Not doing it

Realise that localhost traffic, is, in practice quite secure. Browsers typically refuse http://localhost and http://127.0.0.1 URLs (to prevent JS loaded from internet probing your local servers).

You'll still need at least one DNS entry, localhost.kosunen.fi that resolves to 127.0.0.1 / ::1, browsers will happily accept http://localhost.kosunen.fi even though host name resolves to 127.0.0.1.

What are the risks?

Someone running wireshark on client machine -- if someone has privileges, your model is done for anyway.

Someone hijacks or poisons DNS -- sets it up so that www.kosunen.fi resolves to correct ip, but localhost.kosunen.fi resolves to their internet ip. They steal requests user's browser makes and can include JS.

Mitigate that ad hoc -- only serve data from localhost, not scripts. Set up restrictive CORS/XSRF/CSRF.

You are still left with CORS for HTTP x HTTPS there are solutions to that.

Super-simple CORS

Here between 2 ports, 4040 and 5050, that's just as distinct as between different hosts (localhost vs your.com) or protocols (HTTPS vs HTTP). This is the cloud server:

import bottle


@bottle.route("/")
def foo():
    return """
<html><head></head>
<body><p id="42">Foobar</p>
<script>
fetch("http://localhost:5050/").then(
    function(response) {
        console.log("status " + response.status);
        response.json().then(
            function(data) {
                console.log(data);
            }
        );
    }
);
</script>
</body></html>""".strip()

bottle.run(host="localhost", port=4040, debug=True)

And this is localhost server:

import bottle


@bottle.route("/")
def foo():
    bottle.response.headers["Access-Control-Allow-Origin"] = "*"  # usafe!
    bottle.response.headers["Access-Control-Allow-Methods"] = "HEAD, GET, POST, PUT, OPTIONS"
    bottle.response.headers["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token"
    return """{"foo": 42}"""

bottle.run(host="localhost", port=5050, debug=True)

Making it safe(r): in the localhost server, read request Origin, validate it, e.g. starswith("https://your.com/") and then return same Allow-Origin as request Origin. IMO that ensures that a compliant browser will only serve your localhost content to JS loaded in your.com context. A broken browser, or, any program running on same machine can, of course, trick your server.

Admissible answered 26/1, 2016 at 11:30 Comment(2)
One cert per installation would be infeasible for us, unfortunately. Can you elaborate on ways to get around the protocol restrictions of CORS? The original plan was to do it just through http as the data itself isn't particularly sensitive, but I couldn't find an acceptible workaround.Kandis
Old hack: JSONP (script src=xxx, always GET request, outgoing data encoded into URL, incoming data is response body + js wrapper). New way is CORS, I'll try to set up a demo ;-)Admissible
C
1

The best way to go about this is to create a self-signed certificate on your local hostname and add an exception to that in IE.

There are a few services that offer 'localhost over SSL', but these all require the private key to be shared by all users using the service, effectively making the key unusable from a security perspective. You might not care about that too much as long as you only allow connections on the local network interface, but CA's try and revoke these certificates as they compromise the integrity of SSL (see for instance http://readme.localtest.me/).

It should be possible to make a mixed-content (HTTPS to HTTP) CORS request on IE11 (see https://social.technet.microsoft.com/Forums/ie/en-US/ffec5b73-c003-4ac3-a0fd-d286d9aee89a/https-to-http-cors-issue-in-ie10?forum=ieitprocurrentver). You just have to make sure that both sites are in a trusted zone and allow mixed-content. I'm not so sure if the web application can / should be trusted to run mixed-content, so the self-signed certificate is more explicit and provides a higher level of trust!

You can probably not use a real certificate signed by a trusted CA since there is no way for a CA to validate your identity based on a hostname that resolves to 127.0.0.1. Unless you create a wildcard certificate on a domain name (i.e. *.mydomain.com) that also has a subdomain local.mydomain.com resolving to your local machine, but this might interfere with existing SSL infrastructure already in place on mydomain.com.

If you already have a certificate on a hostname, it might be possible to set that hostname to 127.0.0.1 in your client's hosts file to work around this, but again this is more trouble and less explicit than just making a self-signed certificate and trusting that.

BTW: Don't rely on the security of the localhost hostname for the self-signed key as discussed here: https://github.com/letsencrypt/boulder/issues/137. The reason behind this is that some DNS resolvers send localhost requests to the network. A misconfigured or compromised router could then be used to intercept the traffic.

Confined answered 26/1, 2016 at 10:36 Comment(3)
Can't rely on being able to alter users' browser settings, nor on which browser the user will be using other than mandating that it's new enough to support CORS. What we have currently (still in development) is a local.mydomain.com cert that points to 127.0.0.1 via DNS. It works fine, and I've found a couple of other pieces of software that do it this way. Not sure it's the best but it seems to be safer than adding a custom trusted root CA.Kandis
In that case a wildcard certificate on *.mydomain.com will work with local.mydomain.com. You just have to ensure that your private key is securely stored inside your service since you are distributing your private wildcard key to every client machine.Confined
Although debatable, I don't believe this solution is safer than a trusted self-signed certificate. The wildcard could be vulnerable to DNS poisoning and potentially affect your other *.mydomain.com subdomains if leaked from any client machine. Using a self-signed certificate ensures that the risk is limited to the client machine and the hostname used. You can make the service install the self-signed certificate on installation.Confined

© 2022 - 2024 — McMap. All rights reserved.