# Documentation Django 5.2.8

# Signatures cryptographiques

La règle d’or de la sécurité des applications web est de ne jamais se fier à des données de provenance douteuse. Il peut parfois être pratique de transférer des données par un canal non sécurisé. Les valeurs signées cryptographiquement peuvent être transmises de manière fiable au travers d’un canal non sécurisé en sachant que toute modification de donnée sera détectée.

Django offre à la fois une API de bas niveau pour la signature de valeurs et une API de haut niveau pour la génération et la lecture de cookies signés, l’un des usages les plus courants de la signature dans les applications web.

La signature peut également être utile dans les situations suivantes :

- Génération d’URL de « récupération de compte » pour l’envoi à des utilisateurs qui ont perdu leur mot de passe.

- Garantie de non-altération des données stockées dans des champs de formulaire cachés.

- Génération d’URL à usage unique pour autoriser un accès temporaire à une ressource protégée, par exemple pour un fichier téléchargeable payé par un utilisateur.

## Protection de `SECRET_KEY` et de `SECRET_KEY_FALLBACKS`

Lorsque vous créez un nouveau projet Django avec `startproject`, le fichier `settings.py` est généré automatiquement et contient une valeur aléatoire pour le réglage `SECRET_KEY`. Cette valeur est la clé de la sécurisation des données signées, il est donc essentiel de garder cette information secrète, faute de quoi des attaquants pourraient l’utiliser pour générer leurs propres valeurs signées.

`SECRET_KEY_FALLBACKS` peut être utilisé pour une rotation de clés secrètes. Ces valeurs ne seront pas utilisées pour signer des données, mais quand il est indiqué, elles seront utilisées pour valider des données signées ; elles doivent donc être conservées de manière sécurisée.

## Utilisation de l’API de bas niveau

Les méthodes de signature de Django se trouvent dans le module `django.core.signing`. Pour signer une valeur, commencez par créer une instance de `Signer`:

    >>> from django.core.signing import Signer
    >>> signer = Signer()
    >>> value = signer.sign("My string")
    >>> value
    'My string:v9G-nxfz3iQGTXrePqYPlGvH79WTcIgj1QIQSUODTW0'

La signature est collée à la suite de la chaîne de caractères, en utilisant les deux-points comme séparateur. Vous pouvez récupérer la valeur originale au moyen de la méthode `unsign`:

    >>> original = signer.unsign(value)
    >>> original
    'My string'

Si vous passez une valeur non textuelle à `sign`, la valeur sera d’abord forcée à une chaîne avant d’être signée et le résultat de `unsign` donnera cette valeur textuelle :

    >>> signed = signer.sign(2.5)
    >>> original = signer.unsign(signed)
    >>> original
    '2.5'

Si vous souhaitez protéger une liste, un tuple ou un dictionnaire, vous pouvez le faire en utilisant les méthodes sign_object()` et `unsign_object()`:

    >>> signed_obj = signer.sign_object({"message": "Hello!"})
    >>> signed_obj
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:bzb48DBkB-bwLaCnUVB75r5VAPUEpzWJPrTb80JMIXM'
    >>> obj = signer.unsign_object(signed_obj)
    >>> obj
    {'message': 'Hello!'}

Consultez Protection de structures de données complexes pour plus de détails.

Si la signature ou la valeur a été modifiée d’une manière ou d’une autre, une exception `django.core.signing.BadSignature` sera levée :

    >>> from django.core import signing
    >>> value += "m"
    >>> try:
    ...     original = signer.unsign(value)
    ... except signing.BadSignature:
    ...     print("Tampering detected!")
    ...

Par défaut, la classe `Signer` utilise le réglage `SECRET_KEY` pour générer des signatures. Il est possible d’utiliser un clé secrète différente en la transmettant au constructeur de `Signer`:

    >>> signer = Signer(key="my-other-secret")
    >>> value = signer.sign("My string")
    >>> value
    'My string:o3DrrsT6JRB73t-HDymfDNbTSxfMlom2d8TiUlb1hWY'

class Signer(*, key=None, sep=':', salt=None, algorithm=None, fallback_keys=None)

  Returns a signer which uses `key` to generate signatures and `sep` to separate values. `sep` cannot be in the URL safe base64 alphabet. This alphabet contains alphanumeric characters, hyphens, and underscores. `algorithm` must be an algorithm supported by `hashlib`, it defaults to `'sha256'`. `fallback_keys` is a list of additional values used to validate signed data, defaults to `SECRET_KEY_FALLBACKS`.

### Utilisation du paramètre `salt`

Si vous ne voulez pas que plusieurs occurrences d’une chaîne donnée aient toutes la même empreinte de signature, vous pouvez utiliser le paramètre facultatif `salt` de la classe `Signer`. L’utilisation d’un sel (salt) combiné à la clé `SECRET_KEY` va nourrir et renforcer la fonction de hachage pour la signature :

    >>> signer = Signer()
    >>> signer.sign("My string")
    'My string:v9G-nxfz3iQGTXrePqYPlGvH79WTcIgj1QIQSUODTW0'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:bzb48DBkB-bwLaCnUVB75r5VAPUEpzWJPrTb80JMIXM'
    >>> signer = Signer(salt="extra")
    >>> signer.sign("My string")
    'My string:YMD-FR6rof3heDkFRffdmG4pXbAZSOtb-aQxg3vmmfc'
    >>> signer.unsign("My string:YMD-FR6rof3heDkFRffdmG4pXbAZSOtb-aQxg3vmmfc")
    'My string'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I'
    >>> signer.unsign_object(
    ...     "eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I"
    ... )
    {'message': 'Hello!'}

Using salt in this way puts the different signatures into different namespaces. A signature that comes from one namespace (a particular salt value) cannot be used to validate the same plaintext string in a different namespace that is using a different salt setting. The result is to prevent an attacker from using a signed string generated in one place in the code as input to another piece of code that is generating (and verifying) signatures using a different salt.

Au contraire de `SECRET_KEY`, le paramètre `salt` n’a pas besoin de rester secret.

### Vérification de valeurs horodatées

`TimestampSigner` est une sous-classe de `Signer` qui ajoute un horodatage signé à la valeur. Cela permet de confirmer qu’une valeur signée a été créée dans un espace de temps défini :

    >>> from datetime import timedelta
    >>> from django.core.signing import TimestampSigner
    >>> signer = TimestampSigner()
    >>> value = signer.sign("hello")
    >>> value
    'hello:1stLqR:_rvr4oXCgT4HyfwjXaU39QvTnuNuUthFRCzNOy4Hqt0'
    >>> signer.unsign(value)
    'hello'
    >>> signer.unsign(value, max_age=10)
    SignatureExpired: Signature age 15.5289158821 > 10 seconds
    >>> signer.unsign(value, max_age=20)
    'hello'
    >>> signer.unsign(value, max_age=timedelta(seconds=20))
    'hello'

class TimestampSigner(*, key=None, sep=':', salt=None, algorithm='sha256')
  sign(value)

    Signe la valeur `value` et lui ajoute l’horodatage actuel.

  unsign(value, max_age=None)

    Checks if `value` was signed less than `max_age` seconds ago, otherwise raises `SignatureExpired`. The `max_age` parameter can accept an integer or a `datetime.timedelta` object.

  sign_object(obj, serializer=JSONSerializer, compress=False)

    Code, comprime si désiré, ajoute l’horodatage actuel et signe la structure de données complexe (par ex. une liste, un tuple ou un dictionnaire).

  unsign_object(signed_obj, serializer=JSONSerializer, max_age=None)

    Checks if `signed_obj` was signed less than `max_age` seconds ago, otherwise raises `SignatureExpired`. The `max_age` parameter can accept an integer or a `datetime.timedelta` object.

### Protection de structures de données complexes

Si vous souhaitez protéger une liste, un tuple ou un dictionnaire, c’est réalisable en utilisant les méthodes `Signer.sign_object()` et `unsign_object()`, ou les fonctions `dumps` et `loads` du module de signature (qui sont elles-mêmes des raccourcis vers `TimestampSigner(salt='django.core.signing').sign_object()/unsign_object()`). Elles utilisent la sérialisation JSON en arrière-plan. Le format JSON assure que même quand la clé `SECRET_KEY` a été dérobée, un attaquant ne pourra pas provoquer l’exécution de code arbitraire en exploitant le format pickle :

    >>> from django.core import signing
    >>> signer = signing.TimestampSigner()
    >>> value = signer.sign_object({"foo": "bar"})
    >>> value
    'eyJmb28iOiJiYXIifQ:1stLrZ:_QiOBHafwucBF9FyAr54qEs84ZO1UdsO1XiTJCvvdno'
    >>> signer.unsign_object(value)
    {'foo': 'bar'}
    >>> value = signing.dumps({"foo": "bar"})
    >>> value
    'eyJmb28iOiJiYXIifQ:1stLsC:JItq2ZVjmAK6ivrWI-v1Gk1QVf2hOF52oaEqhZHca7I'
    >>> signing.loads(value)
    {'foo': 'bar'}

En raison de la nature de JSON (il n’existe pas de distinction native entre les listes et les tuples), même si vous passez un tuple, vous obtiendrez une liste dans le résultat de `signing.loads(object)`:

    >>> from django.core import signing
    >>> value = signing.dumps(("a", "b", "c"))
    >>> signing.loads(value)
    ['a', 'b', 'c']

dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False)

  Renvoie une chaîne JSON utilisable dans une URL, signée et compressée en base64. L’objet sérialisé est signé par `TimestampSigner`.

loads(string, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None, fallback_keys=None)

  Inverse de `dumps()`, génère `BadSignature` si la vérification de la signature échoue. Vérifie `max_age` (en secondes) si ce paramètre est défini.