nemesifier
nemesifier

Reputation: 8539

Is there a function for generating settings.SECRET_KEY in django?

I wrote an ansible-role for openwisp2 to ease its deployment, it's a series of django apps. To ease the deployment as much as possible, I wrote a simple (probably trivial) SECRET_KEY generator script:

#!/usr/bin/env python
"""
Pseudo-random django secret key generator
"""
from __future__ import print_function
import random

chars = 'abcdefghijklmnopqrstuvwxyz' \
        'ABCDEFGHIJKLMNOPQRSTUVXYZ' \
        '0123456789' \
        '#()^[]-_*%&=+/'

SECRET_KEY = ''.join([random.SystemRandom().choice(chars) for i in range(50)])

print(SECRET_KEY)

which is called by ansible to generate the secret key the first time the ansible playbook is run.

Now, that works fine BUT I think it defeats the built-in security measures Django has in generating a strong key which is also very hard to guess.

At the time I looked at other ways of doing it but didn't find much, now I wonder: is there a function for generating settings.SECRET_KEY in django?

That would avoid this kind of home baked solutions that even though they work they are not effective when it comes to security.

Upvotes: 86

Views: 64127

Answers (6)

Rizqi N. Assyaufi
Rizqi N. Assyaufi

Reputation: 1030

Run this command below on the Terminal.

$ python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'

Example output:

2x$e%!k_u_0*gq0s4!_u(2(^lpy&gir0hg)q&5nurj0-sseuav

Upvotes: 60

parth5757
parth5757

Reputation: 21

Ensure that the SECRET_KEY is defined in your settings.py . It should look something like this:

# settings.py

# Make sure to generate a complex, random SECRET_KEY
import os

SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", default='your-default-secret-key')

Generate a Secure Secret Key

import secrets
print(secrets.token_urlsafe(50))

& replace new genrated key in with settings.py secret_key

Upvotes: 2

Rik Schoonbeek
Rik Schoonbeek

Reputation: 4510

Note that this may not be safe to use for production, as S Ghosh is pointing out in a post below. But copy and paste this after running django-admin shell for example to quickly get a key.

from django.core.management.utils import get_random_secret_key  
get_random_secret_key()

Upvotes: 141

hlongmore
hlongmore

Reputation: 1856

The post referenced by S Ghosh TLDR: Generate Django Secret Key indicates that as of version 3.1.3, Django is actually using the Python secrets module behind the scenes. Looking at this blob for get_random_secret_key and this other blob for get_random_string, I can see it is so:

def get_random_secret_key():
    """
    Return a 50 character random string usable as a SECRET_KEY setting value.
    """
    chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
    return get_random_string(50, chars)
def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS):
    """
    Return a securely generated random string.
    The bit length of the returned value can be calculated with the formula:
        log_2(len(allowed_chars)^length)
    For example, with default `allowed_chars` (26+26+10), this gives:
      * length: 12, bit length =~ 71 bits
      * length: 22, bit length =~ 131 bits
    """
    return ''.join(secrets.choice(allowed_chars) for i in range(length))

The only issue with the get_random_secret_key function as I see it in the code is that the allowed characters does not include capital letters, so the number of possible bits for the same size key is smaller than it would be if capitals were included, but not by much:

from math import log2
lower_plus_numbers = (list(chr(o) for o in range(0x61, 0x7B))
                      + list(chr(o) for o in range(0x30, 0x3A)))
punctuation = list('!@#$%^&*(-_=+)')
upper_alpha = list(chr(o) for o in range(0x41, 0x5B))
shorter = log2((len(lower_plus_numbers) + len(punctuation)) ** 50)
longer = log2((len(lower_plus_numbers) + len(punctuation) + len(upper_alpha)) ** 50)
print(f'longer: {int(longer + 0.5)}; shorter: {int(shorter + 0.5)} '
      f'difference: {int(longer - shorter + 0.5)}; ratio: {longer/shorter}')

The output of the above code:

longer: 312; shorter: 282; difference: 30; ratio: 1.1070316647619918

So, if you have a recent enough Django and Python, the biggest question is whether you want to generate your SECRET_KEY with a dependency on Dango, or just Python. If you don't mind the Django dependency, but want to include upper case letters, or want to have a longer key, you can easily do something like:

from django.utils.crypto import get_random_string
key_length = 60
get_random_string(
    key_length,
    allowed_chars=lower_plus_numbers + punctuation + upper_alpha,
)

Sample output:

'gW(VDtylhoAuZNcLbIC=ai5=2*tPZ=Gmf4D1^4T!NxX3tB0%_w7pYY2+FgDx'

If you don't want the Django dependency, you could use S Ghosh's answer. Or if you want more than hex characters, you could do something like:

allowed_chars = [chr(i) for i in range(0x21, 0x7F)]
key_length = 60
key = ''.join(secrets.choice(allowed_chars) for i in range(key_length))

Value of key (as a python string):

'DN7tbWid#q6R^=%i"[1AA>$@AZg=XD+p|[aB?:#V`:kKWL77P6dC,~(\\9O\'j'

Upvotes: 18

S Ghosh
S Ghosh

Reputation: 464

I would like to add that as per the commit Fixed #31757 -- Adjusted system check for SECRET_KEY to warn about autogenerated default keys, the method get_random_secret_key() is considred to be insecure.

If you are using python 3.6+ then you can use the secrets.token_hex([nbytes=None]) function

python3 -c 'import secrets; print(secrets.token_hex(100))'

credit TLDR: Generate Django Secret Key

Upvotes: 27

Daniel Roseman
Daniel Roseman

Reputation: 600059

Indeed, you can use the same function that generates a new key when you call startproject, which is django.core.management.utils.get_random_secret_key().

Note however it's not very different from your version.

Upvotes: 62

Related Questions