Reputation: 8539
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
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
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
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
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
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
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