Some checks failed
CI / php-lint (push) Successful in 19s
CI / secret-scan (pull_request) Successful in 8s
CI / secret-scan (push) Successful in 10s
CI / static-tests (push) Successful in 30s
CI / php-lint (pull_request) Successful in 19s
CI / auto-merge (push) Has been skipped
CI / static-tests (pull_request) Successful in 30s
CI / auto-merge (pull_request) Failing after 4s
Ferme le finding HIGH de la revue Produits (#17) : le PIN d'action sensible etait verifie sans limitation de tentatives. Conception via panel multi-agents (3 lentilles + synthese + passe adversariale, holds=true) puis revue de l'implementation (holds=true). Dimension du throttle = UTILISATEUR AGISSANT (identite de session, RG-T02), pas l'email cible (contournable par rotation) ni l'IP (collateral sur poste partage). Table dediee pin_throttle (entite 22) STRICTEMENT SEPAREE des compteurs de login (user.failed_login_attempts / login_throttle) : un echec de PIN n'incremente aucun compteur de connexion (pas d'escalade DoS vers le login). - db/migrations/0002_pin_throttle.sql : table cle sur actor_user_id (UNIQUE, FK -> user ON DELETE CASCADE), separee du login. Appliquee a la base dev. - ThrottlePolicy : dimension 'pin' (bornes propres PIN_THROTTLE_*, 30s..300s, plus permissives que le login : controle de dissuasion, residuel Faible). - PinThrottle (nouveau) : isLocked / recordFailure (upsert atomique + backoff, une transaction, miroir d'AuthService) / reset (UPDATE simple). N'ecrit jamais user/login_throttle/audit_log. - PinVerifier::payTimingDecoy : parite de timing du chemin verrouille. - ProductController update/destroy : gate AVANT verification (leurre + 422 generique, pas de pin.failed sous verrou actif = borne anti-flood de l'audit) ; recordFailure sur PIN faux ; reset sur succes, cle sur l'acteur de SESSION. - Docs Merise 21 -> 22 entites : RG-T22 (mlt), entite 22 pin_throttle (mcd/mld/dictionary), couverture MCT 22/22 (mct). - .env.example + docker-compose : PIN_THROTTLE_THRESHOLD/BASE/MAX/WINDOW. - Journal RNCP : docs/journal/2026-06-15--p3-throttle-pin-rg-t22.md. Tests : 188 verts (525 assertions), PHPStan L6 propre.
39 lines
2.3 KiB
SQL
39 lines
2.3 KiB
SQL
-- db/migrations/0002_pin_throttle.sql
|
|
-- =============================================================================
|
|
-- Wakdo - Migration 0002 : pin_throttle (entite 22, RG-T22)
|
|
-- =============================================================================
|
|
-- Purpose : Throttle des tentatives de PIN d'action sensible, par UTILISATEUR
|
|
-- AGISSANT (identite de session authentifiee, GuardResult->userId).
|
|
-- STRICTEMENT SEPARE des compteurs de connexion
|
|
-- (user.failed_login_attempts / user.lockout_until / login_throttle)
|
|
-- pour qu'un echec de PIN ne verrouille jamais la CONNEXION d'un
|
|
-- compte (pas d'escalade DoS sur la surface plus sensible). Sibling de
|
|
-- login_throttle (4.21) : meme forme, dimension differente (l'acteur,
|
|
-- pas l'IP). Le runner db/migrate.sh applique *.sql dans l'ordre
|
|
-- lexicographique via la table schema_migrations.
|
|
-- Target : MariaDB 11.4 LTS, InnoDB, utf8mb4 / utf8mb4_unicode_ci.
|
|
-- =============================================================================
|
|
|
|
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
|
|
CREATE TABLE pin_throttle (
|
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
actor_user_id INT UNSIGNED NOT NULL,
|
|
failed_attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
|
window_started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
lockout_until DATETIME NULL,
|
|
last_attempt_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY uk_pin_throttle_actor_user_id (actor_user_id),
|
|
KEY idx_pin_throttle_lockout_until (lockout_until),
|
|
CONSTRAINT fk_pin_throttle_actor_user_id FOREIGN KEY (actor_user_id)
|
|
REFERENCES user (id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- Note : pas de seed. La cle est l'acteur (un user back-office authentifie), donc
|
|
-- la FK ON DELETE CASCADE est sure (contrairement a login_throttle, dont la cle
|
|
-- est une IP arbitraire et qui n'a pas de FK). La purge cron des lignes sans
|
|
-- verrou actif au-dela de THROTTLE_PURGE_AFTER_HOURS s'aligne sur login_throttle :
|
|
-- DELETE FROM pin_throttle
|
|
-- WHERE (lockout_until IS NULL OR lockout_until < NOW())
|
|
-- AND last_attempt_at < NOW() - INTERVAL <THROTTLE_PURGE_AFTER_HOURS> HOUR;
|