2

I want to create a simple python app that is compiled into an .exe file and when opening the file it will ask the user a password before it runs the code.

This happens locally without a server, so the credentials need to be kept either in a seperate text file or inside the compiled .exe file, but I don't want the password to show as a string when decompiling the .exe file.

How can I do this?

Anon1223
  • 37
  • 3
  • This doesn't work. They can simply edit the exe and eliminate your check. This is happening for more than 40 years. Only environments like secure enclaves can solve your issue. – kelalaka Jan 08 '22 at 23:30
  • Your option is to encrypt the file and ask for a password to decrypt it correctly, however, this works only once since they can copy the decrypted result... – kelalaka Jan 08 '22 at 23:33

2 Answers2

1

I would suggest using a hash algorithm, like SHA256, to ensure that the password isn't stored as plaintext in the binary. SHA256 isn't the best algorithm in the world for hashing passwords, but then again, neither is storing the hash locally.

Here's how to generate a hash using the hashlib library:

import hashlib
  
password = "enter-your-password-here"
result = hashlib.sha256(password.encode())
print(result.hexdigest())
BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
  • "SHA256 isn't the best algorithm in the world for hashing passwords." So why suggest it then? Surely Python has support for argon2, scrypt and the like. Please don't forget that SO answers are all too often copied into production code without further research. – Peter Jan 09 '22 at 16:57
  • Because OP asked for a method to prevent the password from showing up in plaintext in the binary, and SHA256 does that. Even if a better hash algorithm was chosen, the storing of the hash in the binary would remain a major weakness. – BrokenBenchmark Jan 09 '22 at 20:06
1

The simplest of easy-to-use encryption algorithms is XOR cipher.

You can implement it easily like this:

def xor_key(text_b: bytes, key: bytes):
    return bytes(c ^ key[i % len(key)] for i, c in enumerate(text_b))

OBFUSCATION_KEY = b'xu6Dl32mcd02vs1dv'  # Any long random string.

s = "Hello world"
obfuscated = b'0\x10Z(\x03\x13E\x02\x11\x08T'
print(xor_key(s.encode(), OBFUSCATION_KEY))
print(xor_key(obfuscated, OBFUSCATION_KEY).decode("utf-8"))
# Prints:
# b'0\x10Z(\x03\x13E\x02\x11\x08T'
# Hello world

Thus you can keep b'0\x10Z(\x03\x13E\x02\x11\x08T' as literal in your code, and use xor_key(obfuscated, OBFUSCATION_KEY).decode("utf-8") when you need the "Hello world" literal. It's quite easy to break, but for many non-commercial cases (or for MVP versions of the product) even this might be enough.

There are some more sophisticated (but still easy to use) solutions, like this (and at the end of writing my answer I found this splendid answer). But a more detailed answer depends on what exactly do you need from this encryption (or maybe even hashing) algorithm.

Yevgeniy Kosmak
  • 3,561
  • 2
  • 10
  • 26