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.
1 Answer 1
hexdigest
returns a stringSo you don't need to
str(hash_mac())
each time. Even if it didn't, you would probably want to put thestr
call within thathash_mac
function.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()
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)
You can feed error messages to
sys.exit
:sys.exit('Mac does not match! Exiting...')
will print
Mac does not match! Exiting...
tostderr
and exit the program with a status code of 1. Note that usually, a status code of 0 means success.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')
- if
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()
uid_file.cfg
isn't present you're a bit buggered \$\endgroup\$print('Match MAC and execute program here\n')
. Most likely in a function, sayfoo()
. What could possibly prevent any user to open this file and modifyif __name__ == '__main__': main()
intoif __name__ == '__main__': foo()
? \$\endgroup\$