Wandering Thoughts archives

2021-02-17

TLS certificates specifying hosts via their CommonName field is more or less gone

TLS certificates for hosts and domains must somehow identify what hostname (or names) they're for. Historically there have been two ways to do this. The first way was a specific sub-field, the CN or CommonName, of the certificate's overall Subject Name. This had the problem that it could only have one name. When people started wanting to have TLS certificates that covered more than one name, they invented another mechanism, the Subject Alternative Name (SAN) extension.

As a practical matter, all vaguely modern software that wants to properly validate TLS certificates has supported (and often preferred) Subject Alternative Names for some time. A great many TLS certificates in the wild are for multiple hosts and it's generally unlikely that the host you're connecting to is the one name that the system chose to put in the CN field; software that only supports CN cannot validate those TLS certificates. As a matter of timing, SANs have been theoretically mandatory since 2002 and checking only SANs has been theoretically required since 2011 (which means that since 2011 or earlier, the CN was supposed to always be one of the SANs).

These days, any remaining support for looking at TLS certificate CommonName to validate TLS certificates is getting more and more extinct (and more so than I expected when I started writing this entry). In the browser realm, Chrome apparently turned it off in 58, released in 2017, and then threw out the option to check it again in Chrome 65 (from the comment on my old entry, which was ironically written shortly before Chrome did this). Firefox is said to have removed support in version 48, from August of 2016. Safari apparently stopped looking at CommonName in iOS 13 and macOS 10.15, which I believe date from late 2019. This Go change also talks about how browsers removed it in 2019 ('last year' for a mid 2020 change).

In non-browser TLS code, Go started ignoring CN by default in Go 1.15 (released in August of 2020) and this will be the only option starting in Go 1.17 (to be released in August of 2021), per here. Since Firefox doesn't support CN any more, I assume that NSS doesn't either, since NSS is basically Firefox's underlying TLS implementation. I have no idea what other TLS libraries are doing, but I would expect that many of them will support CommonName for some time to come; TLS libraries are historically behind browser practices. Hopefully they are all following the 2011 requirement to check only SANs when SANs are present (which they should always be in public certificates).

Probably TLS certificates will continue to contain CommonName fields for a long time to come. Having a Subject Name in general is common (although apparently not actually required) and the CN is a standard (although not required) part of the Subject Name, so you might as well throw it in. Even Mozilla and Let's Encrypt (still) have TLS certificates with CNs. However, since I checked this now, the current CA/Browser Forum baseline requirements (version 1.7.3) allow but don't require CommonName (section 7.1.4.2.2, which says that it's 'discouraged, but not prohibited'). Given how conservative most Certificate Authorities are, I expect them to be issuing TLS certificates with CommonName fields until they're required to stop.

(An interested party could scan Certificate Transparency logs to see if there were very many issued certificates without CNs. Probably there are some; someone must have tried it out at some point through an official CA.)

PS: no-common-name.badssl.com has a TLS certificate without a CN, or at least it's supposed to (via), but the TLS certificate is expired right now as I write this entry so it's hard to test how client software behaves. See also, which pointed me to no-subject.labs.vu.nl, which has a currently valid TLS certificate with no Subject Name at all.

tech/TLSCertificateCNMostlyGone written at 23:28:25; Add Comment

When browsers (or at least Firefox) send HTTP Basic Authentication headers

We're long term fans of using HTTP Basic Authentication in Apache, but while I know how to configure it in Apache (and even how to log out of it in Firefox), I haven't really looked into some of the finer details of how it works. In particular, until recently I hadn't looked into when the browser (or at least Firefox) sends the Authorization header in HTTP(S) requests and when it doesn't.

The simple story of how HTTP Basic Authentication (also) works is that when your browser requests a URL protected by Basic Authentication, Apache will answer with a HTTP 401 status and some additional headers. If your browser has relevant credentials cached, it will re-issue the HTTP request with an Authorization header added. If your browser doesn't have the credentials, it will prompt you for login information (in a process that's recently been improved) and then re-issue the request.

Of course this simple story would be rather bad for responsiveness, since it implies that the browser would make two HTTP requests for every URL protected by HTTP Basic Authentication (one without any authorization, which would get a 401, and then a retry with authorization). So browsers don't do that. Instead to some degree they treat the Authorization header like a cookie and preemptively send it along for at least some requests to your website. The question I was curious about was how broadly Firefox did that. Unfortunately for us, the answer is that Firefox doesn't send the Authorization header very broadly.

(This is an appropriate choice for security, of course.)

As far as I can tell from some simple experimentation, Firefox will preemptively send Authorization for any URL under a directory on your site where it's been challenged for HTTP Basic Authentication before (in the same Basic Authentication realm and so on). It won't preemptively send Authorization outside of the hierarchy under those directories. That's kind of abstract, so here's a concrete example.

Suppose I have a website with URLs (among others) of:

/grafana/
/grafana/d/overview/
/grafana/d/pingstatus/
/grafana/d/downhosts/
/alertmanager/
/statics/

All of these URLs other than the /statics/ hierarchy are protected by the same HTTP Basic Authentication, configured once for /grafana/ and everything underneath it and once for /alertmanager/ (and everything underneath it).

If I request the /grafana/d/overview/ dashboard in a clean session, I will get a 401 and then have to authenticate. If I then request the /grafana/d/pingstatus/ dashboard, Firefox will not preemptively send Authorization, because it's not in or under the first URL; instead it will get a 401 and then re-send the request. If I go to /grafana/ (the top level) Firefox will get a 401 again, but now if I go on to /grafana/d/downhosts/, Firefox will preemptively send Authorization because it's under a URL that Firefox has been challenged on.

(If /grafana/d/overview was a page instead of a directory, requesting /grafana/d/pingstatus afterward would preemptively send a Authorization header because they would both be under the /grafana/d/ directory.)

If I request /alertmanager/ or /statics/ after all of this, my Firefox won't send a preemptive Authorization because both of them are outside of /grafana/. Requesting /alertmanager/ without authentication will get a 401 and Firefox will resend the request with the Authorization header, but Firefox will never request /statics/ with an Authorization header. The /statics/ URL is outside of all HTTP Basic authentication directories and the web server itself will never reply with a 401 to trigger Firefox's sending of Authorization.

(If you want to think of it in cookie terms, I believe this is what would happen if the web server set a cookie with a Path= of the initial URL directory and then could add more paths as you clicked around the site.)

In HTTP, the server's HTTP 401 reply to the browser contains no (reliable) information that the browser can use to determine what URL hierarchy is covered by authentication. The HTTP server has no way of telling the browser 'this challenge is for all of /grafana/' (even though Apache knows that); it just gives 401s for all of those URLs when Firefox sends requests without an Authorization header. Eventually Firefox hopefully learns all of the URLs that need Basic Authentication that you (and Grafana) are actually using.

web/BasicAuthWhenSent written at 00:16:24; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.