# Why MAC Addresses Are Bad Hardware IDs

*Originally published on the [PermitCore blog](https://permitcore.dev/blog/why-mac-addresses-are-bad-hardware-ids).*

A MAC address looks like an obvious choice for a device fingerprint — every network interface has one, it's easy to read, and it feels unique. It's also a genuinely poor foundation for node-locked licensing, for reasons that have nothing to do with theory and everything to do with what real machines actually do.

## The four real problems

**It's trivially spoofable.** Changing a MAC address is a single command on every major OS — `ip link set` on Linux, a registry edit or free utility on Windows, `ifconfig` on macOS. A license lock a customer can defeat in ten seconds isn't protecting much.

**A machine usually has more than one.** Wi-Fi adapter, Ethernet port, a virtual adapter from a VPN client, a Bluetooth interface — which one is "the" device identifier? Pick the wrong one (or the one that happens to be active at activation time) and the same physical machine can present a different fingerprint on a different day, depending on which network it's connected to.

**Modern OSes randomize it by default.** Both iOS/macOS and Android now randomize the MAC address per network by default for privacy — a deliberate, permanent feature, not a bug. A hardware ID that changes every time the device joins a new Wi-Fi network is not a hardware ID.

**VMs and containers don't have stable physical hardware to report.** A virtual NIC's MAC is assigned by the hypervisor and is typically the *first* thing that changes when a VM is cloned, migrated, or recreated — exactly the deployment pattern where you most need a fingerprint that survives infrastructure changes, and exactly where a MAC-based one fails hardest.

## What PermitCore's SDKs actually use instead

Not a single identifier — a combination, hashed together so no individual component is exposed or trivially replayable. This is the real method from the .NET SDK, unmodified:

```csharp
public static string GetHardwareId()
{
    var components = new List<string>
    {
        Environment.MachineName,
        Environment.OSVersion.Platform.ToString(),
        Environment.ProcessorCount.ToString(),
        GetOrCreateSeedFile(),   // a GUID written once, persisted locally
    };
    // + the Windows registry MachineGuid, on Windows

    var combined = string.Join("|", components.Where(c => !string.IsNullOrEmpty(c)));
    var hash = SHA256.HashData(Encoding.UTF8.GetBytes(combined));
    return Convert.ToHexString(hash).ToLowerInvariant();
}
```

Notice what's *not* in that list: no MAC address, no network-dependent value at all. Machine name and processor count are cheap, stable signals; the seed file (a GUID generated once and written to a local, per-user application-data path) is what actually anchors the fingerprint to this specific install rather than this specific network state — even if the machine changes networks, gets a new NIC, or runs entirely offline, the seed file and the other components stay the same. On Windows, the registry's `MachineGuid` (set once at OS install, not tied to any network interface) adds another stable signal. The whole combination is SHA-256 hashed, so the server only ever sees an opaque fingerprint, never the raw machine details.

## No hardware ID is unbeatable

To be honest about the limits: a sufficiently motivated user with full control of their own machine can still spoof a seed file or fake environment values — no client-side fingerprint is unbeatable against someone willing to reverse-engineer your app. The realistic goal isn't "impossible to defeat," it's "significantly more effort than switching a MAC address in Settings," which shifts the population of people who'll actually bother from "everyone" to "a small minority" — combined with server-side activation-limit enforcement and abuse detection, which is where the real protection layer actually lives. See [How to Prevent License Key Sharing](https://permitcore.dev/blog/prevent-license-key-sharing) for that side of the picture.

What do you use as a device fingerprint in your own desktop app, and has it ever bitten you after a hardware or OS change? 🙂

