Valid HTTPS for local environments
Web browsers really don't like sites with untrusted certificates. Totally understandable on the internet, but that warning badge in the address bar can hide real security issues when working locally. This post will demonstrate how to create your own trusted wildcard certificate for use in local environments.
Despite how complicated X.509 can get, generating this type of certificate is actually fairly straightforward and can be completed in a matter of minutes.
Below is a local development version of the website you're looking at right now.
The web server here is configured with proper encryption, but a generic self-signed certificate.
Unsurprisingly, Chrome isn't too thrilled about that. After clicking past the warning page we can see a big red "Not Secure" badge in the address bar.
Sure, it's annoying to look at, but the real problem lies with a form on this page which is submitted insecurely over HTTP. Chrome would usually change the icon in the address bar to indicate this, but the invalid certificate warning takes precedence so no such behavior occurs.
Here's where a trusted certificate would come in handy.
While I'd love to have a single certificate that applies to all *.local domains, this isn't possible, so I'll be settling for *.dev.local instead. After some research online, I pieced together this minimal configuration file which I've saved to dev.cnf.
[req] distinguished_name = req_distinguished_name x509_extensions = v3_ca [req_distinguished_name] commonName = Common Name (e.g. server FQDN or YOUR name) commonName_default = *.dev.local [v3_ca] subjectAltName = @alt_names [alt_names] DNS.1 = *.dev.local
This is essentially the bare minimum to generate a certificate that Chrome and Firefox won't complain about. For more complicated use cases, a proper PKI with root and intermediate certificate authorities may be more appropriate - two excellent resources for this can be found here and here.
Now it's just a matter of creating the certificate and private key. These two files will then be copied to the web server. The certificate will also need to be imported locally, but more on that later.
$ openssl req -batch -x509 -nodes -days 1095 -sha256 \ > -newkey ec:<(openssl ecparam -name prime256v1) \ > -keyout dev.key -out dev.crt -config dev.cnf Generating an EC private key writing new private key to 'dev.key' -----
There should be a dev.crt and dev.key file in the same directory as dev.cnf now.
The dev.crt and dev.key files should be copied over to the web server, then the web server configuration should be updated accordingly.
# nginx ssl_certificate /etc/nginx/ssl/dev.crt; ssl_certificate_key /etc/nginx/ssl/dev.key;
Always be sure to test the updated configuration before reloading the webserver.
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful $ sudo systemctl reload nginx
Finally, I imported the certificate into my local machine to grant it trust.
To do this on Windows:
- Double-click the dev.crt certificate file and select 'Install Certificate'.
- Set the Store Location to Local Machine and click Next.
- Select 'Place all certificates in the following store' and click Browse.
- Select 'Trusted Root Certification Authorities' and click OK.
- Click Next, then Finish. You may also need to restart your browser.
There's also an extra step for Firefox since it doesn't use the system certificate store by default. Simply navigate to "about:config" and enable the "security.enterprise_roots.enabled" option.
With the trusted certificate now in place, the page loads without any security prompts.
That insecure form I mentioned earlier is a lot easier to spot now. A similar warning in the address bar will also occur if content on the page is loaded over HTTP rather than HTTPS.
With the form taken care of, the page is now fully secure!
One thing to keep in mind is the certificate we generated only applies to *.dev.local but not, for example: test.project.dev.local. For this, you just need to add another entry into the 'alt_names' section of the OpenSSL configuration file - i.e. "DNS.2 = *.project.dev.local"