Magnetometer is a platform sensor available under the Generic Sensor API. Magnetometer measures strength and direction of the magnetic field at device's location. The interface offers sensor readings using three properties: x, y, and z. Each returns a number that describes the magnetic field aroud the particular axis. The numbers have a double precision and can be positive or negative, depending on the orientation of the field. The total strength of the magnetic field (M) can be calculated as M = sqrt(x^2 + z^2 + y^2). The unit is in microtesla (µT).
The Earth's magnetic field ranges between approximately 25 and 65 µT. Concrete values depend on location, altitude, weather, interference made by other electric. devices, etc. While we consider it is unlikely that someone determines the precise location of the device from the Mangetometer values, its data can be used for fingerprinting. For instance, it can be determined wheter the device is moving or not. In case of a stationary device, we can make a fingerprint from the device's orientation. Another fingerprintable value is the average total strength of the field, which should remain stable if the device is at the same position and in the same environment.
To protect the device, we are wrapping the x, y, z getters of the
Magnetometer.prototype object. Instead of using the original data, we use
artificially generated values that look like actual sensor readings.
At every moment, our wrapper stores information about the previous reading. Each
rewrapped getter first checks the
timestamp value of the sensor object. If there
is no difference from the previous reading's timestamp, the wrapper returns the
last measured value. Otherwise, it provides a new fake reading.
We designed our fake field generator to fulfill the following properties:
- The randomness of the generator should be high enough to prevent attackers from deducing the sensor values.
- Multiple scripts from the same website that access readings with the same timestamp must get the same results. And thus:
- The readings are deterministic - e.g., for a given website and time, we must be able to say what values to return.
For every "random" draw, we use the Mulberry32 sen_prng that is seeded with a value
generated from the
domainHash which ensures deterministic behavior for the given
website. First, we choose the desired total strength
M of the magnetic field at
our simulated location. This is a pseudo-random number from 25 to 60 uT, like on
We support two variants of settings the initial axes orientaton: - A pseudorandom draw (RANDOM_AXES_ORIENTATION = true) - the original implementation - Calculation from the faked device rotation (shared by other wrappers) - improved version
For both methods, the orientation is defined by a number from -1 to 1 for each axis:
baseZ. By modifying the above-shown formula, we calculate
multiplier that needs to be applied to the base values to get the desired field.
The calculation is done as follows:
- mult = (M * sqrt(baseX^2 + baseY^2 + baseZ^2) / (baseX^2 + baseY^2 + baseZ^2))
Now, we know that for axis
x, the value should fluctuate around
baseX * mult, etc.
How much the field changes over time is specified by the fluctuation factor (0;1] that can also be configured. For instance, 0.2 means that the magnetic field on the axis may change from the base value by 20% in both positive and negative way.
The fluctuation is simulated by using a series of sine functions for each axis. Each sine has a unique amplitude, phase shift, and period. The number of sines per axis is chosen pseudorandomly based on the wrapper settings. For initial experiments, we used around 20 to 30 sines for each axis. The optimal configuration is in question. More sines give less predictable results, but also increase the computing complexity that could have a negative impact on the browser's performance.
For the given timestamp
t, we make the sum of all sine values at the point
The result is then shifted over the y-axis by adding
base[X|Y|Z] * multiplier to
the sum. The initial configuration of the fake field generator was chosen intuitively
to resemble the results of the real measurements. Currently, the generator uses at
least one sine with the period around 100 us (with 10% tolerance), which seems to be
the minimum sampling rate obtainable using the API on mobile devices. Then, at least
one sine around 1 s, around 10 s, 1 minute, and 1 hour. When more than 5 sines are
used, the cycle repeats using
modulo 5 and creates a new sine with the period around
100 us, but this time the tolerance is 20%. The same follows for seconds, tens of
seconds, minutes, hours. The tolerance grows every 5 sines. For 11+ sines, the tolerance
is 30% up to the maximum (currently 50%). The amplitude of each sine is chosen pseudo-
randomly based on the fluctuation factor described above. The phase shift of each
sine is also pseudo-random number from [0;2PI).
Based on the results, this heuristic returns belivable values that look like actual sensor readings. Nevertheless, the generator uses a series of constants, whose optimal values should be a subject of future research and improvements. Perphaps, a correlation analysis with real mesurements could help in the future.
POSSIBLE IMPROVEMENTS Non-stationary devices can be supported if the baseX,Y,Z is updated with each movement. Do more experiments in real environments and possibly update the reference magnetic field vector, or the sine generator, e.g. by simulating temporary pseudorandom electromagnetic interferences, etc.