Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hide username/password in Python codes?

I have a piece of python code, it uses MySQLdb to connect to a database. The username/password are in the script in plaintext. I want to deliver this code to client but do not want them to know the username/password. What is the best way to do that? If the MySQLdb module can be packaged as well, it would be even better. Let us assume it is on Linux and the client has standard Python interpreter. It does not need to be super safe, not disclosing the username/password in plaintext is good enough. The client need to have Write permission to our database. But the Write operation can only come from our program, we do not want the client to write in arbitrary way.

like image 686
szli Avatar asked Dec 08 '22 12:12

szli


1 Answers

Normally, obfuscating passwords is a very bad idea, for the reasons Erik A. Brandstadmoen explains. This is exactly why every DRM solution—DVD-CSS, Blu-Ray HDCP, Flash RTMPE, etc.—eventually fails. On the other hand, sometimes it's necessary for business or legal reasons, which is exactly why all those DRM solutions were invented in the first place.

You say, "It does not need to be super safe, not disclosing the username/password in plaintext is good enough." And it sounds like you've got some kind of contract relationship with your customer. It's perfectly reasonable that you don't need to actually stop them from doing it, you just want to force them to make enough effort that it demonstrates bad faith on their part or something. This is obviously something you need to get legal advice on—otherwise, you're going to waste effort building something that's not sufficient to give you any legal protection, or waste even more effort building far more than you need for the legal protection you want. But let's assume you've got that legal advice, and now you want to proceed.


You can't give someone an encrypted password without also giving them the key. So the strength of the encryption is almost irrelevant. It will almost always be easier for them to recover the key than to crack the cipher—and even easier for them to just put a breakpoint on the mysqldb login function and capture the decrypted value on the fly. Which means you might as well use something dirt-cheap and dead-simple. For example:

def xor(text, key):
    infkey = itertools.chain.from_iterable(itertools.repeat(key))
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(text, infkey))

user = 'user'
encrypted_passwd = '\x1b\x04\x0a\x18\x12\x16\x19\x01'
key = 'key'

do_login(user, xor(encrypted_passwd, key))

Your lawyer might say that XOR encryption is not a sufficient "best effort" to protect your data, and you have to use, say, AES. If so, do it; it really doesn't matter.

Next, worry about hiding the key and the encrypted password (and maybe the xor function) somewhere in your code: rename them to something innocuous, embed them in the middle of other things, don't make it obvious that they're being used to create the password, etc.

You also might want to protect the interface to the MySQL login function. For example, if you wrap up the decryption code and the mysql_connect together in a simple Cython extension, they can't just break on mysqldb.connect or even _mysql.mysql_connect, which just means they need a C-level debugger instead of a Python-level debugger. You can even use a C or binary obfuscator around the decryption-and-connection code; there are a number of commercial products out there, and if your lawyer says you need to do this, you're better off using a well-known, state-of-the-art product instead of anything custom.


Meanwhile, if you're looking for technical protection rather than legal protection, you can usually gain a lot more benefit by mitigating the exposure rather than trying to avoid it, as dm03514's answer suggests: Restrict the client's access, so even if the user steals the client's credentials, they can only do the same things they could have already done with the client.

For example, let's say you want to prevent users from adding a new Shipping record without adding a corresponding Billing record with the correct links. You have logic in the client to verify that, but if they just grab the password from the client, they can add all the Shipping records they want.

Just move that logic to some middleware, so a single API call verifies and adds both records at once. Then give the client access to that middleware instead of directly to the database, and anyone who steals the client's credentials still can't add invalid Shipping records.

This won't prevent them from accessing you without the client, but, if done right, it will make it useless for them to do so.

like image 146
abarnert Avatar answered Dec 14 '22 23:12

abarnert