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. 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 = False
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 a few lines and change our SECRET_KEY and database settings.

settings.py:

SECRET_KEY = 'terefere/kuku-some_key'
ALLOWED_HOSTS = ['some_site.come']
DEBUG = True
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
...
try:
    from .local_settings import * 
except ImportError:
    print('Error of importing local_settings file'!)
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.

Bonus - Django-Environ Module Library

Django-environ is third-party module which allows to use Twelve-factor methodology to configure Django with environment variables. But you don't need to worry how to set environment data and generally things become less comlicated. Let's take a look how django-environ works. First we need to install library:

pip install django-environ

We don't need to add it to INSTALLED_APPS in settings.py. But we should create .env file and place it into directory with settings.py.

.env:

DEBUG=on
SECRET_KEY=#(y8c83h06$6+%*g*a@xm#4yw%hn@dx_!^^$+pey#()a7&
DATABASE_URL=psql://databaseUser:myDatabasePassword@127.0.0.1:8458/databaseName
Note that we don't use qoutes assigning to variables. And don't forget to add .env to your .gitignore file to prevent it from being added to the repository

As you can see we want to hide database settings and SECRET_KEY. To make it work at the top of settings.py we need to add following lines.

settings.py:

# this going somewhere on the top of the file.
import environ
env = environ.Env(
    # set casting, default value
    DEBUG=(bool, False))
# reading .env file
environ.Env.read_env()
...
#  We are ready to change the DEBUG, SECRET_KEY and DATABASES lines to read their values from the environment.
DEBUG = env('DEBUG')
...
SECRET_KEY = env('SECRET_KEY')
...
DATABASES = {
    'default': env.db(),
}

That's all. Now everything should work smoothly. Simple and easy, isn't it? But django-environ has more possibilities. You can check how powerful library it is here: django-environ.readthedocs.io


Here's some courses you might like: