Django's settings.py file has a some sensitive data which can be completely open to the public if you host your code online somewhere like GitHub. If your project contains your SECRET_KEY (which all Django projects have), database passwords or tokens for third-party API's like Amazon or Twitter you're asking for problems. Such data should be hidden. SECRET_KEY is not ironic name. Make sure that the all sensitive data used in production isn't used anywhere else and avoid committing it to source control. This reduces the number of vectors from which an attacker may acquire your data. So how do we keep secrets secret in Django? There are a few approaches. Let's take a look at them. ​

Hiding secrets in external files

This is one of the simplest ways to hide your data. Instead of hardcoding the secret key in settings.py, we can load it from a file. So first we need to create text file. Let's' name it secret_key.txt (you can place it whenever you want) and copy your SECRET_KEY from your settings.py. Our file should looks like this:

secret_key.txt:

#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&

Yep, that's it. You don't even need to put double quotes around it!

Let's make another file - db_password.txt to hide our database password.

db_password.txt:

myDatabasePassword123_

Another file with just one line. Now we can delete SECRET_KEY and database PASSWORD form settings.py. While now add a few lines of code to import these files into our settings.

settings.py:

with open('/tutorial/secret_key.txt') as f:
    SECRET_KEY = f.read().strip()
​
with open('/tutorial/secret_key.txt') as f:
    DB_PASSWORD = f.read().strip()
​
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'tutorial_db',
        'USER': 'postgres',
        'PASSWORD': DB_PASSWORD,
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Don't forget to add secret_key.txt and db_password.txt to .gitignore file!

We don't need more. Now our Django project is quite safe. Sensitive data are out of VCS.

All right but we created two .txt files and we can have impression that we repeat code in settings.py. How to improve our import from file? Just by using json. So lets do it and create secrets.json file.

secrets.json

{
    "SECRET_KEY": "#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&",
    "DB_PASSWORD": "myDatabasePassword"
}

Now we should make changes in settings.py (of course we don't forget to delete sensitive data form the settings). On the top of the file we should import json library

settings.py

import json
with open('/tutorials/secrets.json') as f:
    SECRETS = json.load(f)
# now we need to assign SECRET_KEY to data from the file
SECRET_KEY = SECRETS['SECRET_KEY']
​
# here we assign data from the file to PASSWORD
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'tutorial_db',
        'USER': 'postgres',
        'PASSWORD': DB_PASSWORD,
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

With using try ... except blocks during opening file you can handle all errors in case reading file fails

And that's all. As you can see it's not rocket science. Let's take a look on another approach which use to import external python file. First we need to create local_settings.py file in the directory where settings.py is placed. We should add it to .gitignore.

local_settings.py

SECRET_KEY = '#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&'
DEBUG = True
ALLOWED_HOSTS = ['localhost']
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'tutorial_db',
        'USER': 'postgres',
        'PASSWORD': 'myDatabasePassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Now we need make a few changes in settings.py. Just add one line and change our SECRET_KEY and database settings.

settings.py

SECRET_KEY = 'terefere/kuku-some_key'
ALLOWED_HOSTS = ['some_site.come']
DEBUG = False
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
...
try: from .local_settings import *
except ImportError:
print('Error'!)

Settings from local_settings.py will override all settings placed above import line!

Remember that in external files you can keep all sensitive data not only SECRET_KEY and database password. And never forgot add external files to git ignore.

Hiding secrets in environment variables

This approach allow give us luxury not to think about .gitignore file and only seemingly looks more complicated than using external files. Before we load sensitive data from environment variables we have to export them there. We can open terminal and use bash commands but our environment variables will disappear when we finish session. We need to export our sensitive data permanently. Let's check how it works by setting SECRET_KEY Environment Variable.

  1. Copy the SECRET_KEY value including the quotes. The key should like that: '#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&'
  2. Windows: open command prompt and type setx SECRET_KEY '#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&'
    Linux: open file .bashrc and add line: export SECRET_KEY '#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&'
    Mac: open file .bash_profile and add line: export SECRET_KEY '#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&'

Files .bashrc and .bash_profile should be in home directory. If not you will need to create it. Remember to reload shell after editing those files. Use command source .bashrc/.bash_profile

Very last thing is just fetch environment variable to django settings

settings.py:

 SECRET_KEY = os.environ.get('SECRET_KEY', "default_value")

Analogically we could do with the other sensitive data in settings.py like database password or API's tokens. Easy and without pain. You could wonder at which stage is good to separate sensitive data from django settings. Experts are saying: the earlier you start this separation, the better for you.

Wanna read the rest?

Gotta have a membership to get the bonus part of this post. Sign up and join da crew!