72 lines
4.1 KiB
Markdown
72 lines
4.1 KiB
Markdown
# 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**
|
|
|
|
1. Se connecter en tant qu'**alice** (membre de `devops` uniquement).
|
|
2. 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.
|
|
3. 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**
|
|
|
|
1. Se connecter en tant qu'**alice** (membre de `devops` uniquement).
|
|
2. Ouvrir les outils développeur du navigateur ou utiliser `curl` / Postman.
|
|
3. Envoyer une requête `POST /secrets` avec `name=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**
|
|
|
|
1. Se connecter en tant qu'**alice** (membre de `devops` uniquement).
|
|
2. Parcourir l'URL `http://127.0.0.1:8000/secrets/1`, `/secrets/2`, à partir de 1 en incrémentant.
|
|
3. L'ID 1 appartient à `devops` → alice peut le voir.
|
|
4. 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**
|
|
|
|
1. Se connecter en tant que **bob** dans deux onglets/navigateurs différents.
|
|
2. Dans les deux onglets, ouvrir le formulaire de rotation du secret `AWS root key` (équipe devops).
|
|
3. Soumettre la rotation dans le premier onglet → succès, la version passe à 2.
|
|
4. 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. |