httpscreate-react-apphsts

HSTS preventing access to a subdomain mapped to localhost using hosts and using self-signed certificate


I am using CRA (Create React App) and am binding it to local.example.com:443 using the following .env file:

HOST=local.example.com
PORT=443
HTTPS=true

This ensures that when I run the CRA scaffold, it attempts to bind to local.example.com:443 and a self-signed certificate is issued. I can accept the self-signed in the browser and have an HTTPS-enabled local site. HTTPS is a necessity for some things, such as Secure cookies etc., so I need it for local development as well as the remote deployment. Also, proxy can be set to example.com so that the local frontend uses the remote backend in testing, which is another motivation for this setup.

This works when the host name (example.com) doesn't have HSTS, but when it does, the un-accepted self-signed certificate which CRA creates, is rejected. The certificate gets created for localhost, that's the first problem, because example.com's HSTS needs the certificate to be issued for example.com to be valid. Firefox:

Firefox does not trust this site because it uses a certificate that is not valid for local.example.com. The certificate is only valid for the following names: localhost, localhost.localdomain, lvh.me, *.lvh.me, [::1], 127.0.0.1, fe80::1

But on top of it, since it is a self-signed certificate, the browser will reject it unless the user accepts it first, which is possible to do without HSTS, because the warning page about a self-signed certificate allows one to do it, but with HSTS, a different warning page is shown (the one showing the error quoted above), which says HSTS won't allow the navigation to go through and you're toast.

In Chrome, it is possible to ignore HSTS by typing letmein on the HSTS warning page, which works, but it is not a solution in Firefox and I wonder if there is a better way.

Can CRA issue the certificate so that it appears as if it was issued for example.com so that example.com's HSTS accepts it? I assume there would still be a problem with it being self-signed, but if this is possible, maybe the warning page about self-signed certificate will be shown instead nad the user will be able to accept the certificate and carry on?


Solution

  • To "bypass" HSTS locally, you have to generate a valid certificate for your local domain.
    The minimal setup would be to generate a self-signed certificate with correct CN and SAN.

    You can achieve this even for a website you don't own. The first website that I found that has HSTS with includeSubDomains directive is www.amazon.com.

    enter image description here

    So I tested a CRA running on local.www.amazon.com.
    For that I generated a private key and certificate with the following command:

    openssl req \
        -x509 \
        -newkey rsa:4096 \
        -sha256 \
        -days 90 \
        -nodes \
        -keyout noca.local.www.amazon.com.key \
        -out noca.local.www.amazon.com.crt \
        -subj '/CN=local.www.amazon.com' \
        -extensions san \
        -config <( \
        echo '[req]'; \
        echo 'distinguished_name=req'; \
        echo '[san]'; \
        echo 'subjectAltName=DNS:local.www.amazon.com')
    

    My .env file looks like this:

    HOST=local.www.amazon.com
    PORT=5000 
    HTTPS=true
    SSL_CRT_FILE=noca.local.www.amazon.com.crt
    SSL_KEY_FILE=noca.local.www.amazon.com.key
    

    I open the crt file with the Keychain (I'm on Mac) and trusted it. You can do that differently on other platforms.

    You will also have to edit the hosts file (/etc/hosts for me) and add the following line:

    127.0.0.1 local.www.amazon.com
    

    And here is the result:

    enter image description here

    EDIT 1:

    If you have to do this for multiple websites you might want to create a local CA that you self-sign.
    And then you will generate all your certificates with that CA.
    The benefit will be that you will just have to trust the CA certificate once (in the keychain or in Chrome CA certificates or whatever is used to trust the certificates)