La réponse ci-dessous concerne principalement les cookies signés , une implémentation du concept de sessions (tel qu'utilisé dans les applications Web). Flask propose à la fois des cookies normaux (non signés) (via request.cookies
et response.set_cookie()
) et des cookies signés (via flask.session
). La réponse comporte deux parties, la première décrit comment un cookie signé est généré, et la seconde est présentée sous la forme d'un contrôle qualité qui aborde différents aspects du schéma. La syntaxe utilisée pour les exemples est Python3, mais les concepts s'appliquent également aux versions précédentes.
Qu'est-ce que SECRET_KEY
(ou comment créer un cookie signé)?
La signature de cookies est une mesure préventive contre la falsification des cookies. Pendant le processus de signature d'un cookie, le SECRET_KEY
est utilisé d'une manière similaire à la façon dont un "sel" serait utilisé pour brouiller un mot de passe avant de le hacher. Voici une description (follement) simplifiée du concept. Le code dans les exemples se veut illustratif. De nombreuses étapes ont été omises et toutes les fonctions n'existent pas réellement. Le but ici est de fournir une compréhension de l'idée générale, les implémentations réelles seront un peu plus impliquées. Gardez également à l'esprit que Flask fait la plupart de ces tâches en arrière-plan. Ainsi, en plus de définir des valeurs pour votre cookie (via l'API de session) et de fournir un SECRET_KEY
, il n'est pas seulement déconseillé de le réimplémenter vous-même, mais il n'est pas nécessaire de le faire:
Signature de cookie d'un pauvre homme
Avant d'envoyer une réponse au navigateur:
(1) Le premier a SECRET_KEY
est établi. Il ne doit être connu que de l'application et doit être maintenu relativement constant pendant le cycle de vie de l'application, y compris lors des redémarrages de l'application.
# choose a salt, a secret string of bytes
>>> SECRET_KEY = 'my super secret key'.encode('utf8')
(2) créer un cookie
>>> cookie = make_cookie(
... name='_profile',
... content='uid=382|membership=regular',
... ...
... expires='July 1 2030...'
... )
>>> print(cookie)
name: _profile
content: uid=382|membership=regular...
...
...
expires: July 1 2030, 1:20:40 AM UTC
(3) pour créer une signature, ajoutez (ou ajoutez) le SECRET_KEY
à la chaîne d'octets du cookie, puis générez un hachage à partir de cette combinaison.
# encode and salt the cookie, then hash the result
>>> cookie_bytes = str(cookie).encode('utf8')
>>> signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
>>> print(signature)
7ae0e9e033b5fa53aa....
(4) Apposez maintenant la signature à une extrémité du content
champ du cookie d'origine.
# include signature as part of the cookie
>>> cookie.content = cookie.content + '|' + signature
>>> print(cookie)
name: _profile
content: uid=382|membership=regular|7ae0e9... <--- signature
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
et c'est ce qui est envoyé au client.
# add cookie to response
>>> response.set_cookie(cookie)
# send to browser -->
Lors de la réception du cookie du navigateur:
(5) Lorsque le navigateur renvoie ce cookie au serveur, supprimez la signature du content
champ du cookie pour récupérer le cookie d'origine.
# Upon receiving the cookie from browser
>>> cookie = request.get_cookie()
# pop the signature out of the cookie
>>> (cookie.content, popped_signature) = cookie.content.rsplit('|', 1)
(6) Utilisez le cookie d'origine avec l'application SECRET_KEY
pour recalculer la signature en utilisant la même méthode qu'à l'étape 3.
# recalculate signature using SECRET_KEY and original cookie
>>> cookie_bytes = str(cookie).encode('utf8')
>>> calculated_signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
(7) Comparez le résultat calculé avec la signature précédemment sortie du cookie qui vient d'être reçu. S'ils correspondent, nous savons que le cookie n'a pas été modifié. Mais si même juste un espace a été ajouté au cookie, les signatures ne correspondent pas.
# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature
(8) S'ils ne correspondent pas, vous pouvez répondre avec un nombre quelconque d'actions, enregistrer l'événement, supprimer le cookie, en émettre un nouveau, rediriger vers une page de connexion, etc.
>>> if not good_cookie:
... security_log(cookie)
Code d'authentification de message basé sur le hachage (HMAC)
Le type de signature généré ci-dessus qui nécessite une clé secrète pour assurer l'intégrité de certains contenus est appelé en cryptographie un code d'authentification de message ou MAC .
J'ai précisé plus tôt que l'exemple ci-dessus est une simplification excessive de ce concept et que ce n'était pas une bonne idée de mettre en œuvre votre propre signature. En effet, l'algorithme utilisé pour signer les cookies dans Flask s'appelle HMAC et est un peu plus complexe que la simple étape par étape ci-dessus. L'idée générale est la même, mais pour des raisons qui dépassent le cadre de cette discussion, la série de calculs est un peu plus complexe. Si vous êtes toujours intéressé par la création d'un bricolage, comme c'est généralement le cas, Python a quelques modules pour vous aider à démarrer :) voici un bloc de départ:
import hmac
import hashlib
def create_signature(secret_key, msg, digestmod=None):
if digestmod is None:
digestmod = hashlib.sha1
mac = hmac.new(secret_key, msg=msg, digestmod=digestmod)
return mac.digest()
La documentation pour hmac et hashlib .
La "démystification" de SECRET_KEY
:)
Qu'est-ce qu'une «signature» dans ce contexte?
C'est une méthode pour s'assurer que certains contenus n'ont pas été modifiés par quiconque autre qu'une personne ou une entité autorisée à le faire.
L'une des formes les plus simples de signature est la " somme de contrôle ", qui vérifie simplement que deux éléments de données sont identiques. Par exemple, lors de l'installation d'un logiciel à partir de la source, il est important de d'abord confirmer que votre copie du code source est identique à celle de l'auteur. Une approche courante pour ce faire consiste à exécuter la source via une fonction de hachage cryptographique et à comparer la sortie avec la somme de contrôle publiée sur la page d'accueil du projet.
Disons par exemple que vous êtes sur le point de télécharger la source d'un projet dans un fichier gzippé à partir d'un miroir Web. La somme de contrôle SHA1 publiée sur la page Web du projet est «eb84e8da7ca23e9f83 ....»
# so you get the code from the mirror
download https://mirror.example-codedump.com/source_code.tar.gz
# you calculate the hash as instructed
sha1(source_code.tar.gz)
> eb84e8da7c....
Les deux hachages sont identiques, vous savez que vous avez une copie identique.
Qu'est-ce qu'un cookie?
Une discussion approfondie sur les cookies dépasserait le cadre de cette question. Je donne un aperçu ici car une compréhension minimale peut être utile pour mieux comprendre comment et pourquoi SECRET_KEY
est utile. Je vous encourage vivement à faire un suivi avec des lectures personnelles sur les cookies HTTP.
Une pratique courante dans les applications Web consiste à utiliser le client (navigateur Web) comme cache léger. Les cookies sont une mise en œuvre de cette pratique. Un cookie est généralement des données ajoutées par le serveur à une réponse HTTP via ses en-têtes. Il est conservé par le navigateur qui le renvoie ensuite au serveur lors de l'émission de requêtes, également via des en-têtes HTTP. Les données contenues dans un cookie peuvent être utilisées pour émuler ce que l'on appelle l' état de l'état, l'illusion que le serveur maintient une connexion continue avec le client. Seulement, dans ce cas, au lieu d'un fil pour maintenir la connexion "active", vous avez simplement des instantanés de l'état de l'application après qu'elle a traité la demande d'un client. Ces instantanés sont transportés dans les deux sens entre le client et le serveur. Dès réception d'une requête, le serveur lit d'abord le contenu du cookie pour rétablir le contexte de sa conversation avec le client. Il traite ensuite la demande dans ce contexte et avant de renvoyer la réponse au client, met à jour le cookie. L'illusion d'une session en cours est ainsi maintenue.
À quoi ressemble un cookie?
Un cookie typique ressemblerait à ceci:
name: _profile
content: uid=382|status=genie
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
Les cookies sont faciles à parcourir à partir de n'importe quel navigateur moderne. Sur Firefox par exemple, allez dans Préférences> Confidentialité> Historique> supprimer les cookies individuels .
Le content
champ est le plus pertinent pour l'application. D'autres champs contiennent principalement des méta-instructions pour spécifier divers champs d'influence.
Pourquoi utiliser des cookies?
La réponse courte est la performance. L'utilisation de cookies minimise la nécessité de rechercher des éléments dans divers magasins de données (caches mémoire, fichiers, bases de données, etc.), accélérant ainsi les choses du côté de l'application serveur. Gardez à l'esprit que plus le cookie est gros, plus la charge utile sur le réseau est lourde, donc ce que vous enregistrez dans la recherche de base de données sur le serveur peut être perdu sur le réseau. Considérez attentivement ce que vous devez inclure dans vos cookies.
Pourquoi les cookies doivent-ils être signés?
Les cookies sont utilisés pour conserver toutes sortes d'informations, dont certaines peuvent être très sensibles. Ils ne sont pas non plus par nature sûrs et nécessitent un certain nombre de précautions supplémentaires pour être considérés comme sécurisés de quelque manière que ce soit pour les deux parties, client et serveur. La signature des cookies résout spécifiquement le problème avec lequel ils peuvent être bricolés pour tenter de tromper les applications serveur. Il existe d'autres mesures pour atténuer d'autres types de vulnérabilités, je vous encourage à en savoir plus sur les cookies.
Comment un cookie peut-il être falsifié?
Les cookies résident sur le client sous forme de texte et peuvent être modifiés sans effort. Un cookie reçu par votre application serveur peut avoir été modifié pour un certain nombre de raisons, dont certaines peuvent ne pas être innocentes. Imaginez une application Web qui conserve les informations d'autorisation sur ses utilisateurs sur les cookies et accorde des privilèges en fonction de ces informations. Si le cookie n'est pas à l'épreuve du bricolage, n'importe qui pourrait modifier le leur pour élever son statut de "role = visiteur" à "role = admin" et l'application ne serait pas plus sage.
Pourquoi est-il SECRET_KEY
nécessaire de signer des cookies?
La vérification des cookies est un peu différente de la vérification du code source de la manière décrite précédemment. Dans le cas du code source, l'auteur original est le dépositaire et le propriétaire de l'empreinte digitale de référence (la somme de contrôle), qui sera rendue publique. Ce que vous ne faites pas confiance, c'est le code source, mais vous faites confiance à la signature publique. Donc, pour vérifier votre copie de la source, vous voulez simplement que votre hachage calculé corresponde au hachage public.
Dans le cas d'un cookie, cependant, l'application ne garde pas la trace de la signature, elle en garde la trace SECRET_KEY
. C'est SECRET_KEY
l'empreinte digitale de référence. Les cookies voyagent avec une signature qu'ils prétendent être légitime. La légitimité signifie ici que la signature a été émise par le propriétaire du cookie, c'est-à-dire l'application, et dans ce cas, c'est cette affirmation en laquelle vous ne faites pas confiance et vous devez vérifier la validité de la signature. Pour ce faire, vous devez inclure un élément dans la signature qui n'est connu que de vous, c'est le SECRET_KEY
. Quelqu'un peut changer un cookie, mais comme il n'a pas l'ingrédient secret pour calculer correctement une signature valide, il ne peut pas l'usurper. Comme indiqué un peu plus tôt, ce type d'empreintes digitales, où en plus de la somme de contrôle, on fournit également une clé secrète,
Et les sessions?
Les sessions dans leur implémentation classique sont des cookies qui ne portent qu'un identifiant sur le content
terrain, le session_id
. Le but des sessions est exactement le même que celui des cookies signés, c'est-à-dire empêcher la falsification des cookies. Les sessions classiques ont cependant une approche différente. Lors de la réception d'un cookie de session, le serveur utilise l'ID pour rechercher les données de session dans son propre stockage local, qui peut être une base de données, un fichier ou parfois un cache en mémoire. Le cookie de session est généralement configuré pour expirer lorsque le navigateur est fermé. En raison de l'étape de recherche de stockage local, cette implémentation des sessions entraîne généralement une baisse des performances. Les cookies signés deviennent une alternative préférée et c'est ainsi que les sessions Flask sont implémentées. En d'autres termes, les sessions Flask sontcookies signés et pour utiliser des cookies signés dans Flask, utilisez simplement son Session
API.
Pourquoi ne pas crypter également les cookies?
Parfois, le contenu des cookies peut être crypté avant d' être également signé . Ceci est fait s'ils sont jugés trop sensibles pour être visibles depuis le navigateur (le cryptage masque le contenu). Cependant, le simple fait de signer des cookies répond à un besoin différent, celui où l'on souhaite maintenir un certain degré de visibilité et de convivialité des cookies sur le navigateur, tout en évitant qu'ils ne soient interférés.
Que se passe-t-il si je change le SECRET_KEY
?
En modifiant le, SECRET_KEY
vous invalidez tous les cookies signés avec la clé précédente. Lorsque l'application reçoit une demande avec un cookie qui a été signé avec un précédent SECRET_KEY
, elle essaiera de calculer la signature avec le nouveau SECRET_KEY
, et les deux signatures ne correspondront pas, ce cookie et toutes ses données seront rejetés, ce sera comme si le navigateur se connecte au serveur pour la première fois. Les utilisateurs seront déconnectés et leur ancien cookie sera oublié, ainsi que tout ce qui est stocké à l'intérieur. Notez que ceci est différent de la façon dont un cookie expiré est géré. Un cookie expiré peut voir son bail prolongé si sa signature est vérifiée. Une signature non valide implique simplement un cookie non valide.
Donc, à moins que vous ne souhaitiez invalider tous les cookies signés, essayez de les conserver SECRET_KEY
pendant de longues périodes.
Qu'est-ce qu'un bien SECRET_KEY
?
Une clé secrète doit être difficile à deviner. La documentation sur les sessions a une bonne recette pour la génération aléatoire de clés:
>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
Copiez la clé et collez-la dans votre fichier de configuration en tant que valeur de SECRET_KEY
.
À moins d'utiliser une clé générée aléatoirement, vous pouvez utiliser un assortiment complexe de mots, de nombres et de symboles, peut-être disposés dans une phrase connue de vous seul, codée sous forme d'octets.
Ne définissez pas le SECRET_KEY
directement avec une fonction qui génère une touche différente à chaque fois qu'elle est appelée. Par exemple, ne faites pas ceci:
# this is not good
SECRET_KEY = random_key_generator()
Chaque fois que votre application est redémarrée, une nouvelle clé lui sera attribuée, annulant ainsi la précédente.
Au lieu de cela, ouvrez un shell python interactif et appelez la fonction pour générer la clé, puis copiez-la et collez-la dans la configuration.