All posts
Deep Dive Hardware IDs

Why MAC Addresses Are Bad Hardware IDs

PC
PermitCore
September 16, 2026 · 6 min read

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:

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 for that side of the picture.

Previous: Metered Software Licensing Explained Next: Revoke a License After a Failed Payment