4.1 KiB
BONUS - Résistance aux attaques
Scénario 1 - Accès direct à un secret via l'URL (IDOR)
Description
Un utilisateur authentifié tente d'accéder à un secret appartenant à une équipe dont il n'est pas membre, en devinant ou en réutilisant un identifiant de secret dans l'URL.
Procédure de test
- Se connecter en tant qu'alice (membre de
devopsuniquement). - Identifier l'identifiant du secret
Mailchimp API(appartenant àmarketing) - il est visible en parcourant la base de données ou en étant connecté en tant que BOB. - En tant qu'alice, accéder à
http://127.0.0.1:8000/secrets/<id_marketing>.
Résultat attendu
HTTP 403 - page d'erreur « Accès refusé. »
Résultat obtenu
HTTP 403 - la route GET /secrets/{secret_id} délègue à services.reveal_secret(), qui appelle check_team_membership(user, secret.team_id). Alice n'étant pas membre de marketing, une AccessDeniedError est levée dans la couche domaine, et la route retourne la page d'erreur 403 sans révéler aucune information sur le secret.
Scénario 2 - Création d'un secret pour une équipe étrangère
Description
Un utilisateur authentifié soumet un formulaire de création de secret en falsifiant le champ team_id pour cibler une équipe dont il n'est pas membre.
Procédure de test
- Se connecter en tant qu'alice (membre de
devopsuniquement). - Ouvrir les outils développeur du navigateur ou utiliser
curl/ Postman. - Envoyer une requête
POST /secretsavecname=test&value=hack&team_id=<id_marketing>.
Résultat attendu
HTTP 403 - accès refusé, secret non créé.
Résultat obtenu
HTTP 403 - la route POST /secrets délègue à services.create_secret(), qui appelle check_team_membership(user, team_id) avant tout chiffrement ou écriture en base. La règle métier bloque la création et la route renvoie la page d'erreur 403.
Scénario 3 - Enumération d'identifiants (ID enumeration)
Description
Un attaquant authentifié tente de lister tous les secrets en parcourant séquentiellement les identifiants (/secrets/1, /secrets/2, /secrets/3…).
Procédure de test
- Se connecter en tant qu'alice (membre de
devopsuniquement). - Parcourir l'URL
http://127.0.0.1:8000/secrets/1,/secrets/2, à partir de 1 en incrémentant. - L'ID 1 appartient à
devops→ alice peut le voir. - L'ID 2 appartient à
marketing→ alice ne devrait pas y avoir accès.
Résultat attendu
/secrets/1→ HTTP 200, valeur déchiffrée affichée (alice est membre de devops)./secrets/2→ HTTP 403, page d'erreur « Accès refusé. »
Résultat obtenu
Conforme aux attentes. La protection n'est pas basée sur l'obscurcissement des identifiants mais sur le contrôle d'accès systématique dans services.reveal_secret() : chaque accès vérifie l'appartenance à l'équipe propriétaire du secret, quelle que soit la valeur de l'identifiant.
Bonus - Conflit de rotation concurrente (Optimistic Locking)
Description
Deux utilisateurs membres d'une même équipe ouvrent simultanément le formulaire de rotation d'un secret et soumettent tous les deux leurs modifications.
Procédure de test
- Se connecter en tant que bob dans deux onglets/navigateurs différents.
- Dans les deux onglets, ouvrir le formulaire de rotation du secret
AWS root key(équipe devops). - Soumettre la rotation dans le premier onglet → succès, la version passe à 2.
- Soumettre la rotation dans le second onglet (qui contient toujours
expected_version=1).
Résultat attendu
Le deuxième envoi est rejeté avec un message de conflit (HTTP 409).
Résultat obtenu
services.rotate_secret() compare expected_version avec la version courante en base. Si elles diffèrent, une ConflictError est levée avant toute modification. En base, la requête UPDATE … WHERE id = ? AND version = ? ne met à jour aucune ligne si la version a changé (rowcount == 0), ce qui constitue un second filet de sécurité. L'utilisateur voit le formulaire rechargé avec le message d'avertissement et la version actuelle du secret.