On ne veut pas mettre sa SECRET_KEY en prod, et utiliser un service pour générer la clé, ça va deux minutes.
Générer une clé secrète:
import random import string def secret_key(size=50): pool = string.ascii_letters + string.digits + string.punctuation return "".join(random.SystemRandom().choice(pool) for i in range(size)) |
Générer une clé secrete avec une commande manage.py
:
from django.core.management.base import BaseCommand, CommandError from polls.models import Question as Poll class Command(BaseCommand): help = 'Generate a secret key' def add_arguments(self, parser): parser.add_argument('size', default=50, type=int) def handle(self, *args, **options): self.stdout.write(secret_key(options['size'])) |
A mettre dans ./votreapp/management/command/generate_secret_key.py
:)
Une fonction pour lire la clé depuis un fichier texte ou générer la clé si elle n’existe pas:
import io import os try: import pwd except ImportError: pass try: import grp except ImportError: pass def secret_key_from_file( file_path, create=True, size=50, file_perms=None, # unix uniquement file_user=None, # unix uniquement file_group=None # unix uniquement ): try: with io.open(file_path) as f: return f.read().strip() except IOError as e: if e.errno == 2 and create: with io.open(file_path, 'w') as f: key = secret_key(size) f.write(key) if any((file_perms, file_user, file_group)) and not pwd: raise ValueError('File chmod and chown are for Unix only') if file_user: os.chown(file_path, uid=pwd.getpwnam(file_user).pw_uid) if file_group: os.chown(file_path, gid=grp.getgrnam(file_group).gr_gid) if file_perms: os.chmod(file_path, int(str(file_perms), 8)) return key raise |
Et une fonction pour récupérer la clé depuis une variable d’environnement ou un fichier:
def get_secret_key( file_path=None, create=True, size=50, file_perms=None, file_user=None, file_group=None, env_var="DJANGO_SECRET_KEY" ): try: return os.environ[env_var] except KeyError: if file_path: return secret_key_from_file(file_path, create=create, size=size) raise |
Le but de cette dernière est d’avoir ça dans son fichier de settings:
SECRET_KEY = get_secret_key('secret_key')
Et de foutre ‘secret_key’ dans son .gitignore.
Comme ça:
- Si on n’a pas de clé secrète, on en génère une.
- Si on a une, elle est dans un fichier qui n’est PAS dans settings.py.
- On peut commiter settings.py. Chaque env de dev et prod a sa clé secrète automatiquement.
- On peut overrider la clé avec une variable d’environnement si on le souhaite.
En attendant, j’ai proposé qu’on ajoute ça a django extensions. Et qui sait, dans le core peut être un jour ?
Au passage on peut noter l’ajout du module secrets dans la 3.6.
Salut,
Moi j’utilise django-environ pour gérer mes settings
@luxcem : secret.choice est un alias de ce que j’utilise dans secret_key(). Mais c’est vrai que c’est une bonne pratique de l’utiliser si on sait qu’il est dispo.
@Oscar LASM: c’est bien si tu utilise les env vars. Mais je m’appercois de plus en plus que 90% des devs ne savent même pas ce que c’est. Déjà sous windows…
Salut Sam,
Désolé suis un débutant, mais une question me taraude.
quelle différence cela fait t’il entre un clef visible ( SECRET_KEY =’bvqsdhkjh…”), et une clef à priori cachée (SECRET_KEY=get_secret_key(‘secret_key’))
ne peut t’on pas de toute façon affiché SCRET_KEY?
et pourquoi du coup ne pas faire en python 3.6 :
from secrets import token_urlsafe
SECRET_KEY = token_urlsafe(37)
la clef serait différente à chaque démmarage
merci d’avance
Ce n’est pas une question de visibilité, c’est juste pour éviter de gérer la génération de la clé sur chaque poste. Si tu as 3 dev, 3 serveurs de prod, un de dev et un de staging, ça fait 8 clés. Si tu utilise ça la clé est générée automatiquement et tu n’as pas à t’en soucier.
Ca évite aussi l’erreur classique de la clé commitée dans git par le stagiaire si secret_key est déjà dans le .gitignore ou la mise en prod où on oublie la clé.
Enfin on ne doit surtout pas changer la clé à chanque lancement, car les mots de passes en bdd de django.contrib.auth sont hashés avec la clé, et un changement invaliderait tous les mots de passe.
Merci à toi Max
parfois les débutants, veulent absolument trouver des trucs simple avant les vrais dev lol
J’ai tenté de changer la clé, j’ai cassé ma session en cours, mais j’ai pu me reconnecter avec le même mot de passe.
Peut être que ce sont des settings additionnels qui lient les MdP à la clé.
Une autre petite question,
si une machine redémarre, les variables d’environnements sont t’elles sauvegardées?
@paulo: Tes variables d’environnements sont sauvegardées du moment que tu les mets dans un fichier et que tu sources celui-ci au démarage de ton contexte d’exécution …. pour cela plusieurs possibilités :
– les mettre dans le bashrc de ton utilisateur
– les mettre dans un fichier X ou Y et sourcer celui-ci à la volée et ensuite lancer ton appli
– etc…
c’est le même mécanisme que quand tu utilises des virtualenv par exemple…
M’étant rendu compte que j’avais pas mal de choses en commun dans mes différents projets django, je me suis fait une surcouche, qui permet entre autres de s’occuper facilement des settings.
Ma surcouche définit pas mal de settings par défaut (a priori viables, genre
USE_TZ = True
), ensuite mon projet donne d’autres settings (ou écrase certains précédents) et je cherche enfin dans des fichiers de conf (./local_settings.{py|ini} et $VIRTUALENV/etc/monprojet/settings.{py|ini}). On a ensuite une fusion de ces 6 différentes sources de settings.Au final :
la grande majorité des settings sont définis une seule fois dans mon socle applicatif,
quelques settings sont overridés dans chaque projet (parfois seulement 3 ou 4),
en dév’, j’ai uniquement un fichier supplémentaire (local_settings.py) à la racine du projet contenant en général
DEBUG=True
,quand je déploie (avec
pip install monprojet
, rien de plus), je dois écrire un fichier de conf’ normal (au format .ini), sachant que j’ai une commande Django qui me donne son emplacement et son contenu avec les valeurs actuelles,je n’ai plus à gérer plusieurs fichiers de settings avec 99% de choses en commun et qu’il faut mettre à jour à chaque modif,
les mises à jour se font via
pip install monprojet --upgrade
(avec migrate et collecstatic) et n’affectent pas le fichier de conf local.Après la fusion, il y a une analyse des settings, pour faire des références entre settings, créer automatiquement des dossiers, … sachant que toutes les strings sont par défaut interprétées comme des
string.format
.Par exemple, les settings fournis par mon socle définissent :
Quand je déploie, j’ai uniquement à écraser DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD, DATABASE_HOST et DATABASE_PORT dans le fichier de settings local (les valeurs par défaut utilisent du sqlite, histoire d’avoir un truc utilisable par défaut).
J’ai également une liste de correspondances entre les settings Python à personnaliser (genre DATABASE_NAME) et le format .ini (
database.name
, pour chercher l’optionname
dans la section[database]
).Grâce à la possibilité d’appeler du code après la fusion, je peux faire des choses assez complexes : si je définis les coordonnées Redis dans la section
[cache]
ou celles du LDAP dans la section[auth]
, j’ajoute les packages àINSTALLED_APPS
et je vérifie que les packages sont installés.Et pour revenir au sujet,
SECRET_KEY
est défini par un callable qui tente de lire un fichier, et s’il n’existe pas, le crée en générant son contenu => une nouvelle clef est générée lors de chaque déploiement et reste persistante sur la machine, de façon transparente.