16
Encryption for Protecting Python Source Code
Python is a great programming language, it has so many uses, but one thing that it doesn’t do well is help protect your hard work from others. Python source code is plain-text, which means that anyone with access to your files can see what you wrote. Not great when you’ve just written the latest advancement in artificial intelligence (AI) or the best machine learning (ML) algorithm on the planet.
Python has a great feature where it first compiles your source code into bytecode; this is a low-level platform-independent representation of your source code. Back in the days when computers were slow, this was helpful, but when trying to distribute secure code this is a problem. Most solutions for securing Python code involve the distribution of .pyc files. Now, this isn’t all that bad as it does take some effort to reverse engineer a .pyc file. However, that still leaves the possibility for reverse engineering of the file to take place.
Bytecode also limits the version of Python your userbase requires to run your code. If your end-users upgrade their Python version then your code may stop working altogether due to the use of pickle; Python’s object serialisation library. This is where SOURCEdefender can help. It is a commercial offering that has been written from the ground up to help protect Python code and to overcome some of the issues you face when changing Python versions such as bytecode magic numbers.
Under the hood, SOURCEdefender scrambles your plain-text source code with AES-256 encryption. AES is a symmetric algorithm that uses the same key for both encryption and decryption (the security of an AES system increases exponentially with key length).
The sourcedefender package is available from PyPi and can be installed in the usual way:
$ pip3 install sourcedefender
Let’s have a look at an example of the encryption process:
$ cat /home/ubuntu/helloworld.py
print("Hello World!")
$
This is a very basic example, but we do not want anyone to get at our source code. We also don’t want anyone to run this code after 1 hour so when we encrypt the file we can enforce an expiration time of 1 hour from now with the --ttl option and we can delete the plaintext .py file after encryption by adding the --remove option.
The command would look like this:
$ sourcedefender encrypt --remove --ttl=1h /home/ubuntu/helloworld.py
SOURCEdefender v7.1.14
Processing:
/home/ubuntu/helloworld.py
$
The --remove option deletes the original .py file. Make sure you use this so you don’t accidentally distribute the plain-text code. Now the file is encrypted, its contents are as follows:
$ cat /home/ubuntu/helloworld.pye
-----BEGIN SOURCEDEFENDER FILE-----
GhP6+FOEA;qsm6NrRnXHnlU5E!(pT(E<#t=
GhN0L!7UrbN"Am#(8iPPAG;nm-_4d!F9"*7
T1q4VZdj>uLBghNY)[;Ber^L=*a-I[MA.-4
------END SOURCEDEFENDER FILE------
$
Once a file has been encrypted, its new extension is .pye so our loader can identify encrypted files. All you need to remember is to include sourcedefender as a Python dependency while packaging your project and import the sourcedefender module before you attempt to import and use your encrypted code.
The usual import system can still be used and you can import encrypted code from within encrypted code so you don’t need to do anything special with your import statements.
$ cd /home/ubuntu
$ ls
helloworld.pye
$ python3
>>> import sourcedefender
>>> import helloworld
Hello World!
>>> exit()
$
Using your own password or salt for encryption.
It’s easy to use your own encryption password and salt. If you do not set these, we generate unique ones for each file you encrypt. Should you wish to set your own, these can be set from either an Environment variable or as a command option:
$ sourcedefender encrypt --remove --password 1234abcd --salt dcba4321 mycode.py
The following does the exact same thing but sets the password/salt from Environment variables instead of on the command line.
$ export SOURCEDEFENDER_PASSWORD="1234abcd"
$ export SOURCEDEFENDER_SALT="dcba4321"
$ sourcedefender encrypt --remove mycode.py
And to import the code you can either set an environment variable (as with the encryption process). You can also set these in your code before the import:
$ python3
>>> import sourcedefender
>>> from os import environ
>>> environ["SOURCEDEFENDER_PASSWORD"] = "1234abcd"
>>> environ["SOURCEDEFENDER_SALT"] = "dcba4321"
>>> import mycode
The password and salt are specific to the next import, so if you want to use different ones for different files, then feel free to encrypt with different values and remember to set the required password/salt before your import.
This is a common concern when dealing with tools such as this. However, SOURCEdefender hooks into the standard Python import process so there is no impact on the performance of your running application. The decryption of your code takes place during the import of your module and encrypted code won’t run any slower once loaded from a .pye file compared to running after loading from a .py or .pyc file.
Links:
16