When connecting to networks, every network device has a defined address to be found in the local network. This is the so called MAC address or hardware address. It can be found via ip link, yielding something like

2: wlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 […]
    link/ether ma:c_:_a:dd:re:ss brd ff:ff:ff:ff:ff:ff

where you find the ma:c_:_a:dd:re:ss for each interface. This address is typically static and unique, wich means for each network you connect to, the operator of the network can recognize you. This means that your online activities over that can also be connected to your name, if the operator has access to your name somehow. An example of this could be the Wifi on trains, where you typically have a named ticket. This way the company must only correlate hardware addresses with names on ticket if they wanted to assign your name to your device. Some supermarkets also experiment with recognizing customers, even if you do not connect to their wifi.

To tackle this problem, one can randomize one’s hardware address regularly. This blogpost shows an exemplary implementation of this with a focus on wifi networks (as there you might roam the most through different networks).

The setup assumed in use for example configurations throughout this blogpost is iwd and systemd-networkd, but information about other setups might be linked where applicable.

Step 1: Scanning for networks

The first point at which your hardware address gets exposed is through scanning for wifi. The solution for this is simple: just use a connection software that randomizes your hardware address for network scans or, even better, uses passive scans so that not even a scan is sent from your hardware, but instead your network chip only listenes which access points are in range. Such a software is for example iwd, which by default only uses passive scans and if an active scan is necessary (for example because the hardware doesn’t support passive scanning) the hardware address is randomized as well as the SSID for the scan is set to the wildcard instead of leaking your saved networks.

For smartphones this is also simple, iPhones introduced hardware randomization for scans in iOS 8, Android can do this since Android 8 (and it is measurably enabled for Stock Android on a Moto Z2 Force as well as on LineageOS).

NetworkManager also seems to be capable of hardware randomization, but it must explicitly be enabled.

Step 2: Connecting

Until iwd implements hardware address randomization also upon connect, there are several things that can be done, at least for traditional Linuxes.

Randomizing address at every boot

Using systemd-networkd, we can set up a file /etc/systemd/network/00-wifi.link containing

# /etc/systemd/network/00-wifi.link
[Match]
MACAddress=ma:c_:_a:dd:re:ss

[Link]
Name=wifi
MACAddressPolicy=random

with ma:c_:_a:dd:re:ss being the original hardware address of the interface. This does two things: it gives the interface a nicer name (optional) and it assigns a fully randomized address to the interface upon every time the device appears (typically every boot).

Randomizing on every rfkill

We can escalate this further by not only randomizing upon every appearance of the interface, but also every time the interface gets switched off via rfkill (either in hardware via a switch or in software via rfkill or a software switch on the computer). If the device is switched off (as only then we can change the hardware address), we can happily change its hardware address as we like, for example using macchanger.

To automate that, we can implement a udev-rule by creating a file /etc/udev/rules.d/macchanger.rules containing

# /etc/udev/rules.d/macchanger.rules
SUBSYSTEM=="rfkill", ATTR{name}=="phy0", ACTION=="change", ATTR{soft}=="1", RUN+="/usr/bin/macchanger -ab wifi"

This rule checks for changes in the rfkill-subsystem and if the hardware device for your wifi (typically phy0) is shut down/blocked (ATTR{soft}=="1") then we invoke macchanger on that interface to scramble the hardware address. If the interface comes back up again you should see the hardware address having changed.

If you use NetworkManager, there is, as mentioned, also an option to enable network randomization as well.

Remember that this udev rule does not trigger upon the first time the interface comes up. It only triggers each time the interface goes down.

Step 3: Close identifying side channels

Avoid leaking hostename via DHCP

If you want to use hardware address randomization to improve your privacy when roaming different networks, this is only working as long as you do not leak other bits of information that can identify your computer. One of these bits of information, that is most likely pretty unique to your machine is its hostname, which is typically sent when acquiring an IP address from a DHCP-server. To prevent that, we have to stop sending the hostname. For systemd-networkd, this can be achieved by adding SendHostname=false to the [Network]-section of your network-configuration.

This way, DHCP is enabled on your interface without leaking your hostname to the network operator (and also enabling IPv6PrivacyExtensions, which are unfortunately disabled by default).

With this setup, your interface’s hardware address should change regularly enough to significantly improve your privacy, especially if you visit a network more often then once.

Avoid leaking hardware address upon boot

In case you think about implementing only parts of the proposed things, remember that without the 00-wifi.link you will likely leak your hardware address upon boot if you don’t take any other countermeasures (the easiest being said link-file), as the udev-rule will only trigger upon the first down of the interface.

Final remarks

Setting specific hardware addresses

If you ever happen to be on a network requiring a certain hardware address to permit access, you can set any address you like via macchanger once your interface is rfkilled. As the udev-rule above triggers when the interface goes down, the address you set manually will not be overwritten once you bring the interface back up.

iwd >= 0.18

If you use iwd version 0.18 or higher, you currently have to disable iwd’s new interface handling by adding

[General]
use_default_interface=true

to /etc/iwd/main.conf to be able to manage your interface via systemd-networkd and macchanger until iwd implements hardware address randomization also for the connect-stage.

Android and iOS

With a possible inception of iwd on Android devices (in the far future, and stressing the possibly), the option of randomizing hardware addresses for connections as well might come on the table, but until that happens I’m not aware of any easily accessible option to do so at time of writing.