Politique de Mots de Passe
Cette page documente la politique de mots de passe et les mecanismes de securite associes.
Vue d'ensemble
La politique de mots de passe definit les regles pour :
- Complexite minimale des mots de passe
- Expiration et rotation
- Verrouillage apres echecs
- Historique des mots de passe
Regles de complexite
Exigences minimales
| Critere | Exigence |
|---|---|
| Longueur minimum | 8 caracteres |
| Majuscules | Au moins 1 |
| Minuscules | Au moins 1 |
| Chiffres | Au moins 1 |
| Caracteres speciaux | Au moins 1 (!@#$%^&*) |
Mots de passe interdits
- Identique au nom d'utilisateur
- Dans la liste des mots de passe communs
- Identique aux 5 derniers mots de passe
Configuration
Variables d'environnement
# Politique de mots de passe
PASSWORD_MIN_LENGTH=8
PASSWORD_REQUIRE_UPPERCASE=true
PASSWORD_REQUIRE_LOWERCASE=true
PASSWORD_REQUIRE_NUMBER=true
PASSWORD_REQUIRE_SPECIAL=true
PASSWORD_HISTORY_COUNT=5
PASSWORD_EXPIRY_DAYS=90
# Verrouillage de compte
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_MINUTES=15
Configuration Symfony
# config/packages/security.yaml
security:
password_hashers:
App\Entity\User:
algorithm: bcrypt
cost: 13
Validation
Validator Symfony
use Symfony\Component\Validator\Constraints as Assert;
class ChangePasswordRequest
{
#[Assert\NotBlank]
#[Assert\Length(min: 8)]
#[Assert\Regex(
pattern: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/',
message: 'Le mot de passe doit contenir majuscules, minuscules, chiffres et caracteres speciaux'
)]
private string $newPassword;
}
Service de validation
class PasswordPolicyService
{
public function validate(string $password, User $user): array
{
$errors = [];
if (strlen($password) < 8) {
$errors[] = 'Minimum 8 caracteres requis';
}
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = 'Au moins une majuscule requise';
}
if ($password === $user->getEmail()) {
$errors[] = 'Le mot de passe ne peut pas etre identique au email';
}
if ($this->isInHistory($password, $user)) {
$errors[] = 'Ce mot de passe a deja ete utilise recemment';
}
return $errors;
}
}
Verrouillage de compte
Mecanisme
Tentative 1 : Echec → Compteur = 1
Tentative 2 : Echec → Compteur = 2
...
Tentative 5 : Echec → COMPTE VERROUILLE
Champs utilisateur
| Champ | Type | Description |
|---|---|---|
failedLoginAttempts | int | Nombre de tentatives echouees |
lockedUntil | datetime | Date de deverrouillage |
lastFailedLogin | datetime | Derniere tentative echouee |
Implementation
class LoginService
{
public function handleFailedLogin(User $user): void
{
$attempts = $user->getFailedLoginAttempts() + 1;
$user->setFailedLoginAttempts($attempts);
$user->setLastFailedLogin(new \DateTime());
if ($attempts >= 5) {
$user->setLockedUntil(new \DateTime('+15 minutes'));
}
$this->em->flush();
}
public function handleSuccessfulLogin(User $user): void
{
$user->resetFailedAttempts();
$user->setLockedUntil(null);
$this->em->flush();
}
public function isLocked(User $user): bool
{
$lockedUntil = $user->getLockedUntil();
return $lockedUntil && $lockedUntil > new \DateTime();
}
}
Changement obligatoire à la première connexion
Fonctionnement
Lorsqu'un administrateur crée un compte ou réinitialise un mot de passe, l'utilisateur doit obligatoirement changer son mot de passe à la prochaine connexion.
Champ utilisateur
| Champ | Type | Description |
|---|---|---|
mustChangePassword | boolean | Force le changement au prochain login |
Déclencheurs
Le flag mustChangePassword est activé automatiquement lors :
| Action | Activé par |
|---|---|
| Création de compte | Administrateur |
| Réinitialisation mot de passe | Administrateur |
| Réinitialisation par email | Lien de réinitialisation |
Flux utilisateur
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Login │ ──► │ mustChangePassword │ ──► │ Page changement │
│ │ │ == true ? │ │ mot de passe │
└─────────────┘ └─────────────────┘ └─────────────────┘
│
│ false
▼
┌─────────────────┐
│ Dashboard │
└─────────────────┘
Interface utilisateur
Après un login avec mustChangePassword = true :
- Redirection automatique vers
/auth/change-password - Message explicatif affiché
- Formulaire avec :
- Nouveau mot de passe
- Confirmation du mot de passe
- Validation selon la politique de mots de passe
- Redirection vers le dashboard après succès
Configuration
# config/services.yaml
parameters:
force_password_change_on_create: true
force_password_change_on_reset: true
API
GET /api/users/me
Réponse incluant le flag :
{
"id": "...",
"email": "user@example.com",
"mustChangePassword": true
}
Expiration des mots de passe
Configuration
# config/services.yaml
parameters:
password_expiry_days: 90
Verification
public function isPasswordExpired(User $user): bool
{
$lastChange = $user->getPasswordChangedAt();
if (!$lastChange) {
return true;
}
$expiryDate = (clone $lastChange)->modify('+90 days');
return $expiryDate < new \DateTime();
}
Forcer le changement
// Listener qui redirige vers changement de mot de passe
if ($user->getMustChangePassword() || $this->passwordService->isPasswordExpired($user)) {
return new RedirectResponse('/password/change');
}
Historique des mots de passe
Stockage
#[ORM\Entity]
class PasswordHistory
{
#[ORM\Id]
#[ORM\Column(type: 'uuid')]
private UuidInterface $id;
#[ORM\ManyToOne(targetEntity: User::class)]
private User $user;
#[ORM\Column]
private string $passwordHash;
#[ORM\Column]
private \DateTimeInterface $createdAt;
}
Verification
public function isInHistory(string $newPassword, User $user): bool
{
$history = $this->historyRepo->findRecent($user, 5);
foreach ($history as $old) {
if (password_verify($newPassword, $old->getPasswordHash())) {
return true;
}
}
return false;
}
Endpoints API
Changer le mot de passe
POST /api/auth/change-password
{
"currentPassword": "OldPass123!",
"newPassword": "NewPass456@",
"confirmPassword": "NewPass456@"
}
Reinitialiser le mot de passe (admin)
POST /api/admin/users/{id}/reset-password
{
"newPassword": "TempPass789#"
}
Commandes console
Reinitialiser un mot de passe
php bin/console app:reset-user-password user@example.com NewPassword123!
Verifier les comptes expires
php bin/console app:check-password-expiry
Messages utilisateur
| Situation | Message |
|---|---|
| Mot de passe trop court | Le mot de passe doit contenir au moins 8 caracteres |
| Manque majuscule | Le mot de passe doit contenir au moins une majuscule |
| Compte verrouille | Compte verrouille. Reessayez dans X minutes |
| Mot de passe expire | Votre mot de passe a expire. Veuillez le changer |
| Historique | Ce mot de passe a deja ete utilise recemment |
Bonnes pratiques
:::tip Recommandations
- Hashage fort : Utilisez bcrypt avec cost >= 12
- Pas de stockage clair : Jamais de mot de passe en clair
- Transport securise : HTTPS obligatoire
- Logs sans mdp : Ne jamais logger les mots de passe
- Rate limiting : Limitez les tentatives de login :::
Audit
Evenements loggues
| Evenement | Niveau | Donnees |
|---|---|---|
| Login reussi | INFO | email, IP, timestamp |
| Login echoue | WARNING | email, IP, tentative # |
| Compte verrouille | WARNING | email, IP |
| Mot de passe change | INFO | email, par_qui |
| Mot de passe reinitialise | WARNING | email, par_qui |