248

I did a few google searches and checked out the docs ( https://docs.djangoproject.com/en/dev/ref/settings/#secret-key ), but I was looking for a more in-depth explanation of this, and why it is required.

For example, what could happen if the key was compromised / others knew what it was?

Piper
  • 1,266
  • 3
  • 15
  • 26
David542
  • 104,438
  • 178
  • 489
  • 842
  • 8
    If you have a secret key, and it's compromised and released to others, you have a problem. It doesn't matter if you're using Django or not. – Jared Farrish Sep 12 '11 at 00:01
  • 74
    But what problem, exactly? – tobych Nov 15 '11 at 22:27
  • 13
    I did a thorough answer [here](http://stackoverflow.com/questions/15170637/effects-of-changing-djangos-secret-key/15383766?noredirect=1#comment21743494_15383766) (shameless plug) – sberder Mar 14 '13 at 03:18
  • 4
    @sberder Maybe you should write an answer to this question as well. I imagine you could do it much better than the accepted non-answer. – kasperd Jul 04 '15 at 15:14

3 Answers3

128

It is used for making hashes. Look:

>grep -Inr SECRET_KEY *
conf/global_settings.py:255:SECRET_KEY = ''
conf/project_template/settings.py:61:SECRET_KEY = ''
contrib/auth/tokens.py:54:        hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) +
contrib/comments/forms.py:86:        info = (content_type, object_pk, timestamp, settings.SECRET_KEY)
contrib/formtools/utils.py:15:    order, pickles the result with the SECRET_KEY setting, then takes an md5
contrib/formtools/utils.py:32:    data.append(settings.SECRET_KEY)
contrib/messages/storage/cookie.py:112:        SECRET_KEY, modified to make it unique for the present purpose.
contrib/messages/storage/cookie.py:114:        key = 'django.contrib.messages' + settings.SECRET_KEY
contrib/sessions/backends/base.py:89:        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
contrib/sessions/backends/base.py:95:        if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
contrib/sessions/backends/base.py:134:        # Use settings.SECRET_KEY as added salt.
contrib/sessions/backends/base.py:143:                       settings.SECRET_KEY)).hexdigest()
contrib/sessions/models.py:16:        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
contrib/sessions/models.py:59:        if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
core/management/commands/startproject.py:32:        # Create a random SECRET_KEY hash, and put it in the main settings.
core/management/commands/startproject.py:37:        settings_contents = re.sub(r"(?<=SECRET_KEY = ')'", secret_key + "'", settings_contents)
middleware/csrf.py:38:                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
middleware/csrf.py:41:    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
Roshan Mathews
  • 5,788
  • 2
  • 26
  • 36
  • 27
    Why didn't they call it a salt then? ;) – datenwolf Jun 15 '13 at 18:00
  • 51
    This is a guess, but I suppose it is easier to tell people "don't share your `SECRET_KEY`", as opposed to "your `SALT` is a secret key that you should keep to yourself." – Roshan Mathews Jun 25 '13 at 05:47
  • 20
    That distinction is very important. In cryptography, salts are not secret, but `SECRET_KEY` must be kept secure. The use of the `SECRET_KEY` is much more akin to the use of a key in a signed hash such as HMAC (which, if performance wasn't a consideration, would probably be used instead). – Travis Jensen Jun 19 '14 at 18:58
  • 60
    This does not look like an answer to me. All you did was a single grep command without explaining what any of it does. Where is the answer to "what could happen if the key was compromised?"? – kasperd Jul 04 '15 at 15:12
  • Also since the SECRET_KEY is confidential, prefixing a SECRET to the key ensures that Django will encrypt/mask the values where ever needed. – Linus_30 Jun 18 '18 at 09:41
  • @TravisJensen - wouldn't salts not being secret defeat their purpose? It would be trivial to include a known salt in brute force attack along with a password hash dictionary. – Jamie Marshall Dec 30 '20 at 20:56
  • @JamieMarshall for a brute force attack yes, but that is not the reason why hashes are salted. Their purpose is to combat the use of rainbow tables, instead of having to brute force a hash. In that case, it doesn't matter that the salt is public, since you'd still need to fully brute-force the hash using that salt. And if you are using a hashing algorithm that is brute forcible like md5 for security, then you have other issues that have nothing to do with the salt being public. – Rik Schaaf Jan 07 '21 at 21:28
  • 1
    It's not used for passwords. Passwords use random generated salt for each account and PKGF hash by default. But it's used for other stuff, like generating password reset link, etc.. https://github.com/django/django/blob/main/django/contrib/auth/hashers.py#L206 https://github.com/django/django/blob/main/django/contrib/auth/hashers.py#L280 – Marat Mkhitaryan May 22 '21 at 19:49
79

The Django documentation for cryptographic signing covers the uses of the ‘SECRET_KEY’ setting:

This value [the SECRET_KEY setting] is the key to securing signed data – it is vital you keep this secure, or attackers could use it to generate their own signed values.

(This section is also referenced from the Django documentation for the ‘SECRET_KEY’ setting.)

The cryptographic signing API in Django is available to any app for cryptographically-secure signatures on values. Django itself makes use of this in various higher-level features:

  • Signing serialised data (e.g. JSON documents).

  • Unique tokens for a user session, password reset request, messages, etc.

  • Prevention of cross-site or replay attacks by adding (and then expecting) unique values for the request.

  • Generating a unique salt for hash functions.

So, the general answer is: There are many things in a Django app which require a cryptographic signature, and the ‘SECRET_KEY’ setting is the key used for those. It needs to have a cryptographically strong amount of entropy (hard for computers to guess) and unique between all Django instances.

Adam Parkin
  • 17,891
  • 17
  • 66
  • 87
bignose
  • 30,281
  • 14
  • 77
  • 110
  • 2
    "and unique between all Django instances." -- does this imply if say I have 3 webservers running the same Django app behind a load balancer I should have 3 distinct `SECRET_KEY` settings? – Adam Parkin Jul 27 '18 at 19:01
  • 3
    @AdamParkin, that sounds like a good start for [a new question](https://stackoverflow.com/questions/ask), to get its own answer. – bignose Aug 02 '18 at 09:54
  • 2
    Great suggestion, done: https://stackoverflow.com/questions/51657422/are-django-secret-keys-per-instance-or-per-app – Adam Parkin Aug 02 '18 at 15:34
49

According to the Django Documentation on SECRET_KEY:

The secret key is used for:

  • All sessions if you are using any other session backend than django.contrib.sessions.backends.cache, or are using the default get_session_auth_hash().
  • All messages if you are using CookieStorage or FallbackStorage.
  • All PasswordResetView tokens.
  • Any usage of cryptographic signing, unless a different key is provided.

If you rotate your secret key, all of the above will be invalidated. Secret keys are not used for passwords of users and key rotation will not affect them.

Michael B
  • 5,148
  • 1
  • 28
  • 32