5
\$\begingroup\$

I needed a way to make a CLI based script a bit secure. So, I thought of the following workflow:

  • the client will get the MAC of the user, sha256 it and put it in a file at a specific location
  • then in my program, I'm also doing the same thing, check to see if the sha256 of the MAC is present in the file (and if it matches) then let the user use the script.
  • more, that text file will have multiple lines in it (but the only one that maters is the first one)

I'm aware that this might not be the best option for securing a script, but the client said ("do this to make it a bit harder for somebody else to use it").

import hashlib
import sys

from uuid import getnode


def get_mac():
    """return the MAC address of a specific machine"""
    return bytes(str(getnode()), 'utf-8')


def hash_mac():
    """hash the MAC using sha256 algorithm"""
    return hashlib.sha256(get_mac()).hexdigest()


def main():
    with open('uid_file.cfg') as uid_file:
        first_line = uid_file.readline().replace('\n', '')

        if str(hash_mac()) in first_line and len(str(hash_mac())) == len(first_line):
            print('Match MAC and execute program here\n')
        else:
            print('MAC does not match! Exiting...')
            sys.exit(0)


if __name__ == '__main__':
    main()

Any advice on what could go wrong or alternatives / good practices are welcome.

\$\endgroup\$
  • 3
    \$\begingroup\$ well if uid_file.cfg isn't present you're a bit buggered \$\endgroup\$ – Dan Pantry Sep 13 '16 at 12:14
  • \$\begingroup\$ So basically, you will have your business logic instead of print('Match MAC and execute program here\n'). Most likely in a function, say foo(). What could possibly prevent any user to open this file and modify if __name__ == '__main__': main() into if __name__ == '__main__': foo()? \$\endgroup\$ – Mathias Ettinger Sep 13 '16 at 12:54
  • \$\begingroup\$ @MathiasEttinger Yes, basically there will be a function which will contain the rest of the logic. The program will be delivered as a compiled source. So unless somebody reverse-engineer it, there won't be such a case. \$\endgroup\$ – Grajdeanu Alex. Sep 13 '16 at 12:56
  • \$\begingroup\$ What sort of security are you aiming for? That is, what type of attack is this supposed to defend against? \$\endgroup\$ – Xiong Chiamiov Dec 6 '16 at 8:30
6
\$\begingroup\$
  1. hexdigest returns a string

    So you don't need to str(hash_mac()) each time. Even if it didn't, you would probably want to put the str call within that hash_mac function.

  2. You're not using get_mac for any other purpose than hashing it.

    You might thus want to encapsulate this functionality into hash_mac:

    from uuid import getnode as get_mac
    
    def hash_mac():
        """hash the MAC Address using sha256 algorithm"""
        mac = str(get_mac()).encode()
        return hashlib.sha256(mac).hexdigest()
    
  3. You don't need to get a file open for that long.

    You can close it as soon as you read its first line. As a bonus, you can also directly compare strings:

    def main():
        with open('uid_file.cfg') as uid_file:
            first_line = uid_file.readline().strip()
    
        if hash_mac() == first_line:
            print('Match MAC and execute program here\n')
        else:
            print('MAC does not match! Exiting...')
            sys.exit(0)
    
  4. You can feed error messages to sys.exit:

    sys.exit('Mac does not match! Exiting...')
    

    will print Mac does not match! Exiting... to stderr and exit the program with a status code of 1. Note that usually, a status code of 0 means success.

  5. If you want to use it as a form of security, you should avoid giving hints of what to do to unauthorized persons:

    • if uid_file.cfg does not exist, a traceback saying it tried to open it will be printed
    • if content of the file mismatch, a hint is given such that it should contain a MAC address

    You can try to avoid that with a generic error message such as Access denied:

    def main():
        try:
            with open('uid_file.cfg') as uid_file:
                first_line = uid_file.readline().strip()
        except OSError:
            sys.exit('Access denied')
    
        if hash_mac() != first_line:
            sys.exit('Access denied')
    
        print('Match MAC and execute program here\n')
    

Lastly, it might be interesting to define this function as a check that you can easily disable while developping/testing:

import sys
import hashlib
from uuid import getnode as get_mac


def hash_mac():
    """hash the MAC Address using sha256 algorithm"""
    mac = str(get_mac()).encode()
    return hashlib.sha256(mac).hexdigest()


def check_secure_user():
    try:
        with open('uid_file.cfg') as uid_file:
            first_line = uid_file.readline().strip()
    except OSError:
        sys.exit('Access denied')

    if hash_mac() != first_line:
        sys.exit('Access denied')


def main():
    print('Match MAC and execute program here\n')


if __name__ == '__main__':
    check_secure_user()  # deactivate while developping
    main()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.