Pular para o conteúdo principal

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

CritereExigence
Longueur minimum8 caracteres
MajusculesAu moins 1
MinusculesAu moins 1
ChiffresAu moins 1
Caracteres speciauxAu 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

ChampTypeDescription
failedLoginAttemptsintNombre de tentatives echouees
lockedUntildatetimeDate de deverrouillage
lastFailedLogindatetimeDerniere 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

ChampTypeDescription
mustChangePasswordbooleanForce le changement au prochain login

Déclencheurs

Le flag mustChangePassword est activé automatiquement lors :

ActionActivé par
Création de compteAdministrateur
Réinitialisation mot de passeAdministrateur
Réinitialisation par emailLien de réinitialisation

Flux utilisateur

┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Login │ ──► │ mustChangePassword │ ──► │ Page changement │
│ │ │ == true ? │ │ mot de passe │
└─────────────┘ └─────────────────┘ └─────────────────┘

│ false

┌─────────────────┐
│ Dashboard │
└─────────────────┘

Interface utilisateur

Après un login avec mustChangePassword = true :

  1. Redirection automatique vers /auth/change-password
  2. Message explicatif affiché
  3. Formulaire avec :
    • Nouveau mot de passe
    • Confirmation du mot de passe
  4. Validation selon la politique de mots de passe
  5. 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

SituationMessage
Mot de passe trop courtLe mot de passe doit contenir au moins 8 caracteres
Manque majusculeLe mot de passe doit contenir au moins une majuscule
Compte verrouilleCompte verrouille. Reessayez dans X minutes
Mot de passe expireVotre mot de passe a expire. Veuillez le changer
HistoriqueCe mot de passe a deja ete utilise recemment

Bonnes pratiques

:::tip Recommandations

  1. Hashage fort : Utilisez bcrypt avec cost >= 12
  2. Pas de stockage clair : Jamais de mot de passe en clair
  3. Transport securise : HTTPS obligatoire
  4. Logs sans mdp : Ne jamais logger les mots de passe
  5. Rate limiting : Limitez les tentatives de login :::

Audit

Evenements loggues

EvenementNiveauDonnees
Login reussiINFOemail, IP, timestamp
Login echoueWARNINGemail, IP, tentative #
Compte verrouilleWARNINGemail, IP
Mot de passe changeINFOemail, par_qui
Mot de passe reinitialiseWARNINGemail, par_qui