← Tolerance · Lab
Lab · 09 live

Your Phone's Fingerprint Is Its Flaws

Every other exhibit on this site treats sensor error as something to cancel. This one turns it around. Many phones' motion sensors are calibrated one by one at the factory, to cancel each unit's manufacturing flaws, and the numbers that calibration leaves behind are small, stable, and unique to the phone.

That makes them an ID. Before the fixes described below, a web page could read them without asking, and the ID survived clearing cookies, private browsing, and a factory reset. This page shows the method on your own phone's sensor stream, entirely on your device.

Capture

On a phone, press Run the test and follow the instructions: lay the phone flat, flip it over, then stand it on each long edge and each end. The current instruction also sits in a bar at the bottom of the screen, so there's no need to scroll back. The test stops by itself once it has all six poses, after two minutes, or when you leave the page; press Finish to stop sooner. Nothing is read before you press the button or after the test stops. The result appears here, and the panels below show the details.

No phone handy? Simulate a phone runs the same test on a made-up phone with known flaws.

runs on this device · readings are never sent
Waiting. Nothing is read until you press a button.
source
—
samples
0
rate
—
phone is
—
no samples yet

Accelerations are in m/s² and include gravity, which is why a phone lying flat reads about 9.8 on one axis. Rotation rates are in degrees per second; a gyroscope at rest should read zero, and whatever it reads instead is its error. So far the page has been tried on one real phone, an iPhone; everything else was tested with simulated sensor data.

Offset and gain from gravity

Gravity is the one reference everyone carries. Lying flat, a perfect accelerometer reads exactly 1 g on one axis and zero on the other two. Turn it face down and that axis should read exactly −1 g. The average of the two readings is the axis's offset; half the difference, compared with 1 g, is its gain error. A gyroscope is even simpler: at rest it should read zero.

Lay the phone flat, then face down, then stand it on each long edge and each end, holding each pose still for about a second. A face fills in once the page has seen it held still.

No still poses yet.

The gain check assumes standard gravity, 9.80665 m/s². Real gravity runs from about 9.78 m/s² at the equator to 9.83 at the poles, so any gain error smaller than about ±0.3% could be your latitude rather than your sensor. Offsets can also drift with temperature. Both are why the next step doesn't lean on gravity at all.

The grid

Inside the sensor, an analog-to-digital converter turns each reading into a whole number of counts. The phone then multiplies by a calibration gain measured for this unit at the factory, adds an offset, and hands the result to the browser. Strictly the gain is a 3×3 matrix, because each axis also picks up a little of the other two. Either way, the change from one reading to the next is gain × whole number of counts: the offset cancels, even if the browser lets it drift. Zoom in on those changes and they aren't continuous at all. They sit on a grid whose spacing is the factory's gain.

Finding the spacing is a greatest-common-divisor problem with decimals. The page follows the SensorID paper: it guesses each axis's step from the spacing the changes cluster at, rounds every change to whole counts, and refits the whole gain matrix by least squares, letting in bigger changes as the estimate sharpens. Noise from the physical sensor doesn't blur the grid, because that noise happens before the counting: it moves a reading to a different grid point, never between two.

axis
Capture some samples first.

Top: how much one axis changes from one reading to the next, zoomed in until whole steps show as spikes with empty space between them. Bottom: every non-zero change folded onto a single step (where it falls between two whole steps). A grid shows as one tall spike; no grid shows as a flat spread. "Counts per g" is how many steps make up standard gravity, a number set at the factory. "Mixes in" is the largest share of another axis in this one's reading.

Your fingerprint

The grid steps are numbers the factory chose for this one phone, and they don't change: not when you clear cookies, not in a private window, not after a factory reset, because they live in the sensor's calibration, not in the browser. Round each step to 20 parts per million, hash the results, and you have an ID.

Press Keep this one, then capture again. The check compares the steps themselves, so it can tell the same phone from a different one even if a step happens to sit on a rounding boundary and flips the hex.

fingerprint
none yet
Needs a grid on at least three axes.
axes used
—
precision
—
bits, at most
—
kept
—
Nothing kept yet. It lives only in this tab's memory: reload and it's gone.

Bits, at most assumes each gain could fall anywhere in a ±1% band with every 20 ppm slice equally likely: log₂(2% ÷ 20 ppm) ≈ 10 bits per axis. Real gains aren't spread that evenly, and the axes of one chip may not vary independently, so treat it as a ceiling, not a measurement. The SensorID authors measured real ones instead. In 127 iPhone 6S gyroscopes, each axis's gain carried only about 5 bits; the cross-axis terms, which this page's ID leaves out, carried 6.5 to 7 bits each, for about 42 bits from the gyroscope and about 67 with the magnetometer added.

The fix

The GCD trick needs every change between readings to be an exact whole number of counts. So the cure is to break that exactness. The SensorID authors proposed adding random noise, spread evenly between −½ and +½ count, to each count before the calibration is applied: a reading can then land anywhere in its cell instead of on a grid point, the changes stop being whole multiples of anything, and the grid search comes back empty. They report that Apple adopted it in iOS 12.2 and also turned off Safari's motion access by default. Apple's own security note says only that the issue "was addressed with improved motion sensor processing" (CVE-2019-8541).

Their second suggestion suits browsers, which don't know the gain: round each reading to a multiple of the sensor's nominal step. Browsers took a blunter version. The W3C DeviceOrientation spec now requires readings to be limited to 0.1 m/s² and 0.1 °/s, and Chromium rounds them to those multiples, citing exactly this attack in its source. That puts every phone on the same grid, the browser's.

Neither costs ordinary motion features much. Walking swings the accelerometer by a few m/s² each step. On the simulated phone here, half a grid step is about 0.001 m/s², under a thousandth of that, and even rounding to 0.1 moves a reading by at most 0.05. Switch the defenses below: the walk's step count doesn't move, but the grid and the fingerprint above do.

grid found on
—
fingerprint
—
walk: steps
—
biggest nudge
—

The switch changes the readings this page analyses, so the grid and fingerprint panels above follow it. It can't undo anything your own browser or phone already did: if your capture shows no grid with the switch on as delivered, a defense was already there. The walk is simulated with the same phone as the capture (or a stock one), 36 steps in 20 seconds. The noise is the paper's suggestion, up to half a count either way; how much Apple actually adds isn't published.

Try this

  1. Press Simulate a phone, wait for the fingerprint, press Keep this one, then Same simulated phone again. Fresh noise, every reading different, same ID. Now try Another simulated phone.
  2. Look at the raw log during a simulation. The digits look like noise, yet the change between two readings on an axis is a whole number of that axis's step, give or take the sliver of the other two axes it mixes in. Pick an axis in The grid and watch the fold collapse into one spike.
  3. Switch the defense to + noise. The spikes melt into a smooth hump, the fold goes flat, and the fingerprint disappears. The walk still counts 36 steps.
  4. Switch to round to 0.1. A grid comes back on every axis, but it's 0.1 exactly, 98.07 counts per g, and it would be the same on any phone.
  5. On a phone: press Run the test and follow the bar at the bottom of the screen. When it finishes, the result says which of the three cases you got: the phone's own grid, a grid that's only a number format or the browser's rounding, or none at all.
  6. For the gravity check, capture only flat and face down on a book, then lift one end of the book a little and hold it flat-side up again. The amber y offset drifts away from where it was. That's the tilt, not the sensor, which is why a careful calibration turns each axis both ways.

How it works

A stable error is information. Calibration exists because no two sensors come out of the factory alike, so on many phones each one is measured and given its own correction. That correction makes the readings accurate, and it also makes them distinctive, because the correction itself is different for every unit and never changes. Accuracy needs the correction to be applied; privacy needs nobody to be able to read it back. The grid trick reads it back.

Why the grid gives so much away. Gravity can only tell you a gain to a fraction of a percent: your latitude alone shifts it by up to about 0.3%. The grid doesn't depend on gravity at all. Every change between two readings is an exact whole number of counts, so a few hundred readings pin the step far more tightly than gravity can, and the answer comes out the same every time. That combination, precise and repeatable, is what makes an identifier. A slow drift in the offset, by contrast, is useless for tracking, because it doesn't repeat.

The same stream, used honestly. Ride Report turns the same motion-sensor stream into a ride-quality meter on a tray table, and Scroll Report into a rolling 3D seismograph of your phone's motion. Both care about how readings change: bumps, sway, turbulence. Neither cares exactly which grid point a reading lands on. That gap between what motion features need and what fingerprinting needs is why the fixes are cheap: blur the last digit and every honest use still works. Rounding to 0.1 m/s² is the heavier hand: it erases any vibration smaller than that.

Fingerprinting in general. Browsers leak identity through anything that differs between devices and stays put: screen size, installed fonts, how a graphics card draws a curve, how the audio stack renders a test tone. Many of those shift with a software update or a settings change. Calibration is unusual because it's hardware: it survives clearing cookies, private windows and a factory reset. The fix had to come from the platform (Apple's noise, the browsers' rounding and permission prompts), because no page can protect you from another page.

What this page can and can't see. The SensorID fingerprint for iPhones came from the gyroscope and the magnetometer. The authors found their method didn't directly apply to the iPhone's accelerometer, and devicemotion doesn't include the magnetometer at all, so on an iPhone the gyroscope is the part that matters. This page tries the accelerometer too, which the authors could fingerprint on the Google Pixel 2 and 3. Like the paper, it recovers each sensor's full gain matrix, so axes that mix are fine. It skips the paper's last trick: on iPhones with an M-series motion coprocessor the matrix entries are whole multiples of 2−16 °/s, which let the authors round a slightly blurred estimate (Safari's offset drifted) back to the exact values.

What to expect on your phone. On an iPhone in Safari, the accelerometer sat on a grid of 1/65536 g on all three axes (exactly in one capture, to within a few parts in ten thousand in another). That is fixed-point storage, the same kind of 2−16 resolution the paper found in iPhone gyroscope data, and it is identical on every iPhone, so the page labels it "format" and leaves it out of the ID. Beneath it there was no coarser grid of the phone's own, and the gyroscope showed no grid at all, which fits the noise Apple added in iOS 12.2. A current Chromium-based browser should show the browser's 0.1 grid, going by its source; that hasn't been tried on an Android phone yet. A grid of your phone's own would mean neither defense reached this page.

Privacy. Everything runs on your device. Readings and the fingerprint are never sent anywhere: the only network request is an anonymous visit count when the page loads (GoatCounter, no cookies). The page stores nothing, and the fingerprint exists only in this tab until you close or reload it.

Build plan

  1. Sensor capture. Permission flow (DeviceMotionEvent.requestPermission() on iOS 13+, none needed on most Android browsers), a still-detector, and a raw-sample log view. On desktop, say plainly that there's no sensor.
  2. Offset and gain from gravity. Flat, then face down, then on an edge: fit per-axis bias and scale against ±1 g, and show the error vector next to a perfect sensor.
  3. Grid recovery. Estimate the ADC step per axis from the lattice the samples fall on: approximate GCD over pairwise differences, with a histogram that makes the lattice visible.
  4. Fingerprint and stability. Quantize the recovered values into a short ID, show it as a hex string and a small generated glyph, run a repeat-capture check, and give a rough entropy estimate.
  5. The defense. A toggle that adds the kind of noise Apple added in iOS 12.2, and the grid disappears. Explain why noise defeats the GCD trick while barely hurting motion features like step counting.
  6. Write-up. Tie it back to Ride Report and Scroll Report (the same sensor stream), fingerprinting in general, and the lesson: a stable error is information.

The deep cut

Zhang, Beresford, and Sheret presented SensorID at IEEE Security & Privacy in 2019. From fewer than 100 motion samples, collected and processed in under a second, they recovered a fingerprint that didn't change over half a year, after a factory reset, or after an iOS update. On iPhones it came from the gyroscope and the magnetometer, both calibrated per device at the factory; a Samsung Galaxy S8's gyroscope, by contrast, wasn't. All 870 iOS devices in their data had distinct gyroscope fingerprints, and for the iPhone 6S they estimated about 42 bits from the gyroscope and at least 25 from the magnetometer, about 67 in all. The accelerometers of the Google Pixel 2 and 3 showed per-device calibration too, but with only a few Pixels they couldn't say how unique it was. Most other Android phones, they thought, calibrate only offsets, which this attack can't recover.

Their fixes were the two on this page: noise of up to half a count added before calibration, or readings rounded to a multiple of the sensor's nominal step. They report that Apple adopted the noise in iOS 12.2 (CVE-2019-8541) and turned off Safari's motion access by default; iOS 13 replaced that with the requestPermission() prompt this page uses. Google, told in December 2018, was still investigating when the paper came out. They also noted, citing Das et al., that 2,653 of the 100,000 most visited websites read motion sensors and some already sent the data to their servers, so a fingerprint could be computed after the fact. The W3C spec now requires a permission step and readings limited to 0.1 m/s² and 0.1 °/s.

Earlier work had tried motion sensors as fingerprints by learning their statistical quirks. Bojinov, Michalevsky, Nakibly and Boneh (2014) measured the accelerometers of more than 10,000 phones through JavaScript, but by SensorID's account identified only 53% of devices even with the browser's user-agent string added. Reading the calibration itself, rather than its side effects, is what made SensorID exact. Manufacturing tolerances that engineers calibrate away for accuracy are exactly what make each unit distinguishable.

Reading

  • J. Zhang, A. R. Beresford & I. Sheret, “SensorID: Sensor calibration fingerprinting for smartphones,” IEEE Symposium on Security and Privacy, 2019. doi:10.1109/SP.2019.00072.
  • H. Bojinov, Y. Michalevsky, G. Nakibly & D. Boneh, “Mobile device identification via sensor fingerprinting,” arXiv:1408.1416, 2014.
  • A. Das, G. Acar, N. Borisov & A. Pradeep, “The web's sixth sense: a study of scripts accessing smartphone sensors,” ACM CCS, 2018.
  • Apple, “About the security content of iOS 12.2” (CVE-2019-8541: “A privacy issue existed in motion sensor calibration”), 2019.
  • W3C, DeviceOrientation Event Specification: security and privacy considerations, and the 0.1 m/s² and 0.1 °/s precision limits.
  • Chromium source, services/device/generic_sensor/platform_sensor_util.h: the rounding multiples and the comment explaining them (crbug.com/1018180).