In certain cases it may be possible to obtain high entropy User-Agent Client Hints in the first request from a browser.
One of the biggest issues with User-Agent Client Hints (UA-CH) is that, by their nature, they are sent by browsers only after being requested to do so by a web server. This means that they are necessarily only available on requests subsequent to the first one from a given client. This limitation is by design but severely disrupts many web use cases such as server-side adapation and analytics. This limitation of client hints has been one of the most controversial aspects of the proposal since its inception. Indeed, this is the subject of issue #2 lodged against the proposal (by DeviceAtlas).
Google are aware of this issue and have proposed two mitigations:
- A Critical-CH response header. This response header asks the browser to retry the request if it agrees to furnish the addiitonal requested UA-CH headers. This is not particularly useful since it doesn't help at all with the additional round trip required.
- A TLS handshake mechanism. This is a more useful mitigation in that it can make UA-CH headers available on the first request with certain limitations.
The TLS handshake mechanism uses an extension to the TLS protocol called Application-Layer Protocol Settings or ALPS. As the name suggests, ALPS allows for application-layer protocol settings to be agreed at the TLS level prior to any HTTP requests taking place. ALPS is a Google-led proposal and is being managed in the IETF as an Internet Draft.
Source: Google
BoringSSL and NGINX
At present there is only one TLS library that supports ALPS: BoringSSL, a Google fork of OpenSSL designed to be easier to maintain. BoringSSL is currently the SSL library in use in Chrome/Chromium and Android.
It is possible to compile NGINX against BoringSSL rather than OpenSSL. This, along with some additional configuration directives in a forked verison of NGINX, enables full control of high-entropy User-Agent Client Hints at a TLS level. Once configured correctly this permits high-entropy UA-CHs to be available in the first request from a newly-seen browser.
Implementors should be aware that this TLS ALPS approach does have the limitation that it works only at a global level for a particular domain. As an example, you cannot configure this method to request different UA-CHs for example.com/pathA and example.com/pathB.
Example implementation
Boring-nginx is a version of NGINX built on BoringSSL rather than OpenSSL. Aaron Tagliaboschi has put together a Docker image of BoringNginx with some additional configuration parameters to enable high-entropy UA-CHs to be requested at the TLS level. This is a good starting point for testing implementations of TLS ALPS in conjunction with UA-CH.
DeviceAtlas has tested this approach and can confirm that it works correctly with Chrome.