Fixed identifier for a machine (uuid.getnode)

Question:

I’m trying to find something I can use as a unique string/number for my script that is fixed in a machine and easily obtainable(cross-platform). I presume a machine would have a network card. I don’t need it to be really unique, but the necessary is it should be fixed in a long run and as rare as possible.

I know MAC can be changed and I’d probably make a warning about it in my script, however I don’t expect anyone to change MAC each morning.

What I came up with is uuid.getnode(), but in the docs there is:

If all attempts to obtain the hardware address fail, we choose a random 48-bit number

Does it mean that for each function call I get another random number, therefore it’s not possible to use it if MAC is unobtainable?

…on a machine with multiple network interfaces the MAC address of any one of them may be returned.

Does this sentence mean getnode() gets a random(or first) MAC from all available? What if it’d get MAC A in first run and MAC B next time? There’d be no problem if I’d get a fixed list(sort, concatenate, tadaaa!)

I’m asking because I have no way how to test it myself.

Asked By: Peter Badida

||

Answers:

I managed to test the first part on my android device and on each new python run it created random number, so it’s not usable at all for this purpose.

The second problem kind of drowned itself, because if in the docs it mentioned that it may return any one of them, then it’s not something you could rely on (+I couldn’t find a machine I could test it on). A nice package netifaces came to rescue, which does a similar thing

netifaces.interfaces() # returns e.g. ['lo', 'eth0', 'tun2']

netifaces.ifaddresses('eth0')[netifaces.AF_LINK]
# returns [{'addr': '08:00:27:50:f2:51', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]

However I rather gave up using MACs, I got something rather more stable.

Now to the identifiers:

1) Windows:

Executing this one and getting output may be good enough:

wmic csproduct get UUID

or the one I used and is available in registry (HKEY_LOCAL_MACHINESOFTWAREMicrosoftCryptography):

import _winreg
registry = _winreg.HKEY_LOCAL_MACHINE
address = 'SOFTWARE\Microsoft\Cryptography'
keyargs = _winreg.KEY_READ | _winreg.KEY_WOW64_64KEY
key = _winreg.OpenKey(registry, address, 0, keyargs)
value = _winreg.QueryValueEx(key, 'MachineGuid')
_winreg.CloseKey(key)
unique = value[0]

2) Linux:

/sys/class/dmi/id/board_serial

or

/sys/class/dmi/id/product_uuid

or if not root:

cat /var/lib/dbus/machine-id

3) Android:

If you are working with python and don’t want to mess with Java stuff, then this should work pretty good:

import subprocess
cmd = ['getprop', 'ril.serialnumber']
self.unique = subprocess.check_output(cmd)[:-1]

but if you like Java, then go for this answer although even ANDROID_ID‘s uniqueness is rather debatable if it’s allowed to change, therefore a serial number is most likely a safer bet.

Note that like it’s already mentioned in the linked answer, even ril.serialnumber can be null/empty or non-existing (missing key). Same thing happens even with the official Android API where it’s clearly stated this:

A hardware serial number, if available.

Mac/iPhone:
I couldn’t find any solution as I don’t have access to any of these, but if there is a variable that holds the machine id value, then you should be able to get there with simple subprocess.check_output()

Answered By: Peter Badida

For Mac/iphone you can try below command:

import subprocess
subprocess.check_output("ioreg -rd1 -c IOPlatformExpertDevice | grep -E '(UUID)'", shell=True).split('"')[-2] # for me i got it on list value -2 if having any trouble try getting it with any alternative list element.
Answered By: Gahan

uuid.getnode will return the same value for every call wthinin a single run of your app. If it has to defer to the random algorithm, then you will get a different value when you start a new instance of your app.

The implementation for getNode shows why. This is sort of what the routine looks like in python 3.7 (comments mine, code simplified for clarity)

_node = None

def getnode():

    global _node
    if _node is not None:
        # Return cached value
        return _node

    # calculate node using platform specific logic like unix functions, ifconfig, etc 
    _node = _get_node_from_platform()
    if not _node:
        # couldn't get node id from the system. Just make something up
        _node = _get_random_node() 
    return _node
Answered By: bigh_29

I wouldn’t recommend using a MAC address for a unique machine identifier, since it can change depending on the network being used. Rather, I’d recommend using the machine’s native GUID, assigned by the operating system during install. I wrote a small, cross-platform PyPI package that queries a machine’s native GUID called py-machineid.

Essentially, it looks like this, but with some Windows-specific WMI registry queries for more a accurate ID. The package also has support for hashing the ID, to anonymize it.

import subprocess
import sys

def run(cmd):
  try:
    return subprocess.run(cmd, shell=True, capture_output=True, check=True, encoding="utf-8") 
                     .stdout 
                     .strip()
  except:
    return None

def guid():
  if sys.platform == 'darwin':
    return run(
      "ioreg -d2 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $(NF-1)}'",
    )

  if sys.platform == 'win32' or sys.platform == 'cygwin' or sys.platform == 'msys':
    return run('wmic csproduct get uuid').split('n')[2] 
                                         .strip()

  if sys.platform.startswith('linux'):
    return run('cat /var/lib/dbus/machine-id') or 
           run('cat /etc/machine-id')

  if sys.platform.startswith('openbsd') or sys.platform.startswith('freebsd'):
    return run('cat /etc/hostid') or 
           run('kenv -q smbios.system.uuid')
Answered By: ezekg