Hash, HMAC et chiffrement : sécuriser un token Django

Hash, HMAC et chiffrement : sécuriser un token Django

Une comparaison == sur un hash ne suffit pas à choisir le bon mécanisme. sha256, HMAC, hash salé, chiffrement : chaque approche offre des garanties différentes. Comprendre lesquelles change concrètement la façon de stocker et vérifier un token en Django. Hash simple import hashlib token_hash = hashlib.sha256(token.encode()).hexdigest() Un hash simple est déterministe : la même entrée produit toujours la même sortie. Aucun secret serveur n’est impliqué. Il est impossible de retrouver le token original depuis le hash (sha256 est une fonction à sens unique). Mais si quelqu’un connaît ou devine le token, il peut recalculer le hash et comparer. ...

25 mai 2026 · 4 min · Anthony
Python collections : Counter, defaultdict, deque et les autres

Python collections : Counter, defaultdict, deque et les autres

Le module collections est dans la bibliothèque standard Python depuis la version 2.4. Il expose des structures de données spécialisées qui résolvent des problèmes récurrents sans dépendance externe. Pourtant, beaucoup de développeurs continuent d’écrire des boucles de comptage, des initialisations conditionnelles de clés, ou des classes Point avec x, y, z alors que Counter, defaultdict et namedtuple font exactement cela, mieux et plus lisiblement. Voici les six structures que j’utilise régulièrement, avec les cas où elles changent vraiment quelque chose. ...

22 mai 2026 · 7 min · Anthony
Python shutil : copier, déplacer et archiver des fichiers

Python shutil : copier, déplacer et archiver des fichiers

Quand on a besoin de copier un répertoire, déplacer des fichiers ou créer une archive en Python, la tentation est de passer par subprocess.run(["cp", "-r", ...]) ou os.system("mv ..."). C’est fragile, non portable et inutile : shutil (shell utilities) est dans la bibliothèque standard Python depuis la version 2.3 et gère tout cela proprement. La bibliothèque Python shutil est l’outil de référence pour toutes les opérations de haut niveau sur le système de fichiers. ...

21 mai 2026 · 4 min · Anthony
Python operator : itemgetter, attrgetter et l'art de remplacer les lambdas

Python operator : itemgetter, attrgetter et l'art de remplacer les lambdas

La librairie operator fait partie de la bibliothèque standard de Python depuis toujours, et pourtant beaucoup de développeurs continuent d’écrire lambda x: x[0] ou lambda obj: obj.nom quand une fonction de operator ferait le même travail, en plus rapide et plus lisible. Comprendre ce que cette librairie offre, et comment elle est implémentée, change la façon dont on écrit du code fonctionnel en Python. Ce que contient operator operator expose des fonctions qui correspondent aux opérateurs du langage. operator.add(2, 3) est l’équivalent fonctionnel de 2 + 3, operator.lt(a, b) correspond à a < b. L’intérêt n’est pas de remplacer les opérateurs dans du code arithmétique ordinaire, ce serait absurde. L’intérêt est de pouvoir passer une opération comme argument à une fonction d’ordre supérieur (map, filter, sorted, reduce, functools.partial). ...

20 mai 2026 · 7 min · Anthony
Python dataclasses : field(default_factory) en profondeur

Python dataclasses : field(default_factory) en profondeur

Les dataclasses Python génèrent automatiquement __init__, __repr__ et __eq__ à partir des annotations de type. Dès qu’un attribut doit avoir une valeur par défaut mutable (liste, dictionnaire, ensemble), on se heurte à un problème fondamental du langage. field(default_factory=...) est la solution, et comprendre pourquoi elle est nécessaire change la façon dont on raisonne sur l’initialisation en Python. Le piège des valeurs mutables par défaut En Python, les valeurs par défaut des paramètres de fonction sont évaluées une seule fois au moment de la définition de la fonction, pas à chaque appel. C’est une propriété du langage, pas un bug. ...

19 mai 2026 · 5 min · Anthony
Django : save() ne valide pas — le cycle complet de validation

Django : save() ne valide pas — le cycle complet de validation

Appeler obj.save() après avoir défini des validators et un clean() sur le modèle laisse croire que la validation est garantie. Elle ne l’est pas. Django ne déclenche pas full_clean() lors d’un save(), et ce comportement est délibéré. Comprendre pourquoi change la façon d’architecturer la validation dans un projet. Ce que save() fait réellement Le cycle de vie d’un save() est plus court que ce qu’on imagine : Signal pre_save envoyé field.pre_save() appelé sur chaque champ (auto_now, auto_now_add, etc.) INSERT ou UPDATE SQL selon la présence d’un pk Signal post_save envoyé Aucune validation n’y figure. Ni vérification de blank, ni max_length, ni appel à clean(). C’est aussi vrai pour Model.objects.create() et Model.objects.bulk_create() : les trois méthodes persistent sans valider. (bulk_create() est traité en détail dans Django in_bulk() et bulk_create() si vous travaillez sur des insertions en masse.) ...

18 mai 2026 · 5 min · Anthony
Python __add__ et __iadd__ : copie ou mutation en place

Python __add__ et __iadd__ : copie ou mutation en place

__add__ et __iadd__ définissent deux comportements distincts pour l’addition en Python : l’un crée un nouvel objet, l’autre modifie l’existant. Cette distinction a des conséquences réelles sur les alias et les références partagées, et elle réserve des surprises même aux développeurs expérimentés. add : l’addition qui crée __add__ est appelée par l’opérateur +. Elle doit retourner un nouvel objet et laisser les opérandes inchangés. class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) a = Vector(1, 2) b = Vector(3, 4) c = a + b # nouvel objet Vector(4, 6) print(a.x, a.y) # 1 2 — a est inchangé print(id(a) == id(c)) # False — objets distincts a + b appelle a.__add__(b). Si __add__ n’est pas défini sur a ou retourne NotImplemented, Python essaie b.__radd__(a). ...

15 mai 2026 · 4 min · Anthony
Timing attacks en Django avec constant_time_compare

Timing attacks en Django avec constant_time_compare

Une comparaison == sur un token paraît anodine. En pratique, elle laisse filtrer une information mesurable : le temps d’exécution varie selon le nombre de caractères corrects. C’est le principe d’une timing attack, et c’est suffisant pour qu’un attaquant reconstitue le token caractère par caractère. Le problème : la comparaison qui s’arrête trop tôt Python compare les chaînes caractère par caractère et s’arrête dès qu’une différence est trouvée. def verify_token(request): user_token = request.GET.get('token') valid_token = "secret_abc123" if user_token == valid_token: return JsonResponse({'access': 'granted'}) return JsonResponse({'access': 'denied'}) Concrètement : ...

14 mai 2026 · 3 min · Anthony
Vues matérialisées vs cache Django pour les requêtes lentes

Vues matérialisées vs cache Django pour les requêtes lentes

Le réflexe face à un endpoint de reporting lent, c’est souvent le cache. Un @cache_page, un cache.set(), et le problème disparaît… jusqu’à la prochaine expiration. Cette approche a une limite structurelle que les vues matérialisées PostgreSQL résolvent à la racine. Le problème du cache sur les endpoints analytiques Le cache Django stocke le résultat d’une vue Python. La requête SQL coûteuse s’exécute quand même à chaque expiration du cache. Pour un rapport construit sur plusieurs JOINs et agrégations, ça signifie que le premier utilisateur après chaque cache miss attend plusieurs secondes. ...

13 mai 2026 · 4 min · Anthony
Python @property : de l'encapsulation aux descripteurs

Python @property : de l'encapsulation aux descripteurs

Accéder à r.width et écrire r.width = 15 avec la syntaxe d’un attribut normal, tout en exécutant de la validation ou du calcul derrière : c’est ce que @property apporte. Et quand cette logique doit se partager entre plusieurs classes, les descripteurs entrent en jeu. @property : getters et setters sans friction @property permet d’exposer un attribut calculé ou validé avec la même syntaxe qu’un attribut simple. class Rectangle: def __init__(self, width: float, height: float): self._width = width self._height = height @property def width(self) -> float: return self._width @width.setter def width(self, value: float) -> None: if value <= 0: raise ValueError("La largeur doit être positive.") self._width = value @property def area(self) -> float: return self._width * self._height L’avantage concret : l’API publique ne change pas. Ajouter de la validation sur un attribut existant ne casse aucun code appelant. @property sans setter crée un attribut en lecture seule. @nom.deleter gère la suppression via del. ...

12 mai 2026 · 3 min · Anthony

Newsletter

Reçois les nouveaux articles directement dans ta boite mail.

Pas de spam. Désabonnement en un clic.