Skip to main content

How I helped fix Canadaʼs COVID Alert app

On July 31st, Canada's COVID Alert app was made available for general use, though it does not have support for actually reporting a diagnosis in most provinces, yet.

In Quebec, we can run the tracing part of the app, and if diagnosis codes become available here, the app can retroactively report contact. It uses the tracing mechanism that Google and Apple created together, and in my opinion—at least for now—Canadians should be running this thing to help us all deal with COVID-19. I won't run it forever, but for now, it seems to me that the benefits outweigh the "government can track me" fear (it's not actually tracking you; it doesn't even know who you are), and it's enabled on my phone.

But, before I decided to take this position and offer up my own movement data, I wanted to be sure the app is doing what it says it's doing—at least to the extent of my abilities to be duly diligent. (Note: it's not purely movement data that's shared—at least without more context—but it's actual physical interactions with other people whose phones are available within the radio range of Bluetooth LE.)

Before installing the app on my real daily-carry phone, I decided to put it on an old phone I still have, and to do some analysis on the most basic level of communication: who is it contacting?

In 2015, I gave a talk at ConFoo entitled "Inspect HTTP(S) with Your Own Man-in-the-Middle Non-Attacks", and this is exactly what I wanted to do here. The tooling has improved in the past 5 years, and firing up mitmproxy, even without ever having used it on this relatively new laptop, was a one-liner, thanks to Nix:

nix-shell -p mitmproxy --run mitmproxy

This gave me a terminal-based UI and proxy server that I pointed my old phone at (via the Wifi Network settings, under HTTP proxy, pointed to my laptop's local IP address). I needed to have mitmproxy create a Certificate Authority that it could use to generate and sign "trusted" certificates, and then have my phone trust that authority, by visiting http://mitm.it/ in mobile Safari, and doing the certificate acceptance dance (this is even more complicated on the latest versions of iOS). Worth noting also, is that certain endpoints such as the Apple App Store appear to use Certificate Pinning, so you'll want to do things like install the COVID Alert app from the App Store before turning on the proxy.

Once I was all set up to intercept my own traffic, I visited some https:// URLs and saw the request flows in mitmproxy.

I fired up the COVID Alert app again, and noticed something strange… something disturbing:

shows that the app is accessing clients.google.com

In addition to the expected traffic to canada.ca (I noticed it's using .alpha.canada.ca, but I suspect that's due to the often-reported unbearably-long bureaucratic hassle in getting a .canada.ca TLS certificate, but that's another story), my phone, when running COVID Alert, was contacting Google.

HEAD https://clients4.google.com/generate_204

A little web searching helped me discover that this is a commonly-used endpoint that helps developers determine if the device is behind a "captive portal" (an interaction that requires log-in or payment, or at least acceptance of terms before granting wider access to the Web). I decided that this was probably unintended by the developers of COVID Alert, but it still bothered me that an app, designed for tracking interactions between people['s devices], that the government wants us to run is telling Google that I'm running it, and disclosing my IP address in doing so:

shows that the User Agent header identifies the app as

(Note that the app clearly identifies itself in the User-Agent header.)

A bit more quick research turned up a statement by Canada's Privacy Commissioner:

An Internet Protocol (IP) address can be considered personal information if it can be associated with an identifiable individual. For example, in one complaint finding, we determined that some of the IP addresses that an internet service provider (ISP) was collecting were personal information because the ISP had the ability to link the IP addresses to its customers through their subscriber IDs.

It's not too difficult to imagine that Google probably has enough data on Canadians for this to be a real problem.

I discovered that this app is maintained by the Canadian Digital Service, and that the source code is on GitHub, but that the code itself didn't directly contain any references to clients3.google.com.

It's a React Native app, and I figured that the call out to Google must be in one of the dependencies, which—considering the norm with JavaScript apps—are pleasantly restrained mostly to React itself. I had no idea which of these libraries was calling out to Google.

Now, I could have run this app on the iOS Simulator (which did I end up doing to test my patches, below), but I thought "let's see what my actual phone is doing." I threw caution to the wind, and I ran checkra1n on my old phone, which gave me ssh access, which in turn allowed me to copy the app's application bundle to my laptop, where I could do a little more analysis (note the app is bundled as CovidShield because it was previously developed by volunteers at Shopify and was then renamed by CDS (or so I gather, anyway)).

~/De/C/iphone/CovidShield.app  grep -r 'clients3.google.com' *
main.jsbundle:__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),
e.default=void 0;var t={reachabilityUrl:'https://clients3.google.com/generate_204',
reachabilityTest:function(t){return Promise.resolve(204===t.status)},reachabilityShortTimeout:5e3,
reachabilityLongTimeout:6e4,reachabilityRequestTimeout:15e3};e.default=t},708,[]);

(Line breaks added for legibility.) Note reachabilityUrl:'https://clients3.google.com/generate_204. Found it! A bit more searching led me to a package called react-native-netinfo (which was directly in the above-linked package.json), and its default configuration that sets the reachabilityUrl to Google.

Now that I knew where it was happening, I could fix it.

To make this work the same way, we needed a reliable 204 endpoint that the app could hit, and to keep with the expectation that this app should not "leak" data outside of canada.ca, I ended up submitting a patch for the server side code that the app calls. (It turns out that this was not necessary after all, but I'm still glad I added this to my report.)

I also patched, and tested the app code itself via the iOS Simulator.

I then submitted a write-up of what was going wrong and why it's bad, to the main app repository, as cds-snc/covid-alert-app issue 1003, and felt pretty good about my COVID Civic Duty of the day.

The fine folks at the Canadian Digital Service seemed to recognize the problem and agree that it was something that needed to be addressed. A few very professional back-and-forths later (I'll be honest: I barely knew anything about the CDS and I expected some runaround from a government agency like this, and I was pleasantly surprised), we landed on a solution that simply didn't call the reachability URL at all, and they released a version of the app that fixed my issue!

With the new version loaded, I once again checked the traffic and can confirm that the new version of the app does not reach out to anywhere but .canada.ca.

A mitmproxy flow showing traffic to canada.ca and not google.com