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 the Earth.

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: baseX, baseY, and baseZ. By modifying the above-shown formula, we calculate the 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 x=t. 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.