diff --git a/index.php b/index.php new file mode 100644 index 0000000..10d41aa --- /dev/null +++ b/index.php @@ -0,0 +1,358 @@ + true, + 'httponly' => true, + 'samesite' => 'Strict' +]); +session_start(); + +// ======================== +// CONFIGURATION +// ======================== +require_once __DIR__ . '/config/db.php'; +require_once __DIR__ . '/config/env.php'; +require_once __DIR__ . '/lib/RateLimit.php'; +require_once __DIR__ . '/lib/FileValidator.php'; + +$rateLimit = new RateLimit($pdo); +$fileValidator = new FileValidator(); +$message = ''; + +// Initialiser le token CSRF +if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); +} + +// ======================== +// INSCRIPTION +// ======================== +if (isset($_POST['register'])) { + // Vérifier CSRF + if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { + $message = 'Erreur de sécurité (CSRF)'; + } else { + $username = trim($_POST['registerUsername'] ?? ''); + $password = $_POST['registerPassword'] ?? ''; + + if (strlen($username) < 3 || strlen($username) > 50) { + $message = 'Le pseudo doit faire entre 3 et 50 caractères'; + } elseif (strlen($password) < 8) { + $message = 'Le mot de passe doit faire au moins 8 caractères'; + } elseif (!preg_match('/^[a-zA-Z0-9_-]+$/', $username)) { + $message = 'Le pseudo ne peut contenir que des lettres, chiffres, - et _'; + } else { + // Vérifier si le pseudo existe déjà + $stmt = $pdo->prepare("SELECT id FROM `{$env['TABLE_USERS']}` WHERE pseudo = ?"); + $stmt->execute([$username]); + + if ($stmt->fetch()) { + $message = 'Pseudo déjà utilisé'; + } else { + $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]); + $stmt = $pdo->prepare( + "INSERT INTO `{$env['TABLE_USERS']}` + (pseudo, mot_de_passe, role, date_inscription) + VALUES (?, ?, 'user', NOW())" + ); + $stmt->execute([$username, $hash]); + $message = 'Inscription réussie ! Connectez-vous.'; + } + } + } +} + +// ======================== +// CONNEXION +// ======================== +if (isset($_POST['login'])) { + // Vérifier CSRF + if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { + $message = 'Erreur de sécurité (CSRF)'; + } else { + $username = trim($_POST['loginUsername'] ?? ''); + $password = $_POST['loginPassword'] ?? ''; + $clientIp = $_SERVER['REMOTE_ADDR']; + + // Rate limiting + if ($rateLimit->isBlocked($clientIp, 'login', 5, 900)) { + $message = 'Trop de tentatives. Réessayez dans 15 minutes.'; + } else { + $stmt = $pdo->prepare( + "SELECT * FROM `{$env['TABLE_USERS']}` WHERE pseudo = ?" + ); + $stmt->execute([$username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user || !password_verify($password, $user['mot_de_passe'])) { + $rateLimit->recordAttempt($clientIp, 'login'); + $message = 'Identifiants incorrects'; + } else { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['pseudo']; + $_SESSION['role'] = $user['role']; + $message = 'Connexion réussie !'; + } + } + } +} + +// ======================== +// DÉCONNEXION +// ======================== +if (isset($_POST['logout'])) { + // Vérifier CSRF + if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { + $message = 'Erreur de sécurité (CSRF)'; + } else { + $_SESSION = []; + session_destroy(); + header("Location: " . $_SERVER['PHP_SELF']); + exit; + } +} + +// ======================== +// CRÉATION DE POST +// ======================== +if (isset($_POST['createPost'])) { + // Vérifier CSRF + if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { + $message = 'Erreur de sécurité (CSRF)'; + } elseif (!isset($_SESSION['user_id'])) { + $message = 'Vous devez être connecté pour poster'; + } else { + $userId = $_SESSION['user_id']; + + // Rate limiting : 1 post par minute + if ($rateLimit->isBlocked($userId, 'post', 1, 60)) { + $message = 'Vous ne pouvez poster qu\'une fois par minute. Patientez...'; + } else { + $content = trim($_POST['postContent'] ?? ''); + + if (!$content) { + $message = 'Le contenu du post est vide'; + } elseif (strlen($content) > 5000) { + $message = 'Le post ne doit pas dépasser 5000 caractères'; + } else { + // Insérer le post + $stmt = $pdo->prepare( + "INSERT INTO `{$env['TABLE_MESSAGES']}` + (id_utilisateur, contenu, date_creation) + VALUES (?, ?, NOW())" + ); + $stmt->execute([$userId, $content]); + $postId = $pdo->lastInsertId(); + + // Gérer les fichiers + if (!empty($_FILES['postImage']['tmp_name'])) { + $file = $_FILES['postImage']; + $validationResult = $fileValidator->validate($file, 2 * 1024 * 1024); + + if ($validationResult['valid']) { + // Vérifier la sécurité du fichier + if ($fileValidator->isSafe($file['tmp_name'])) { + // Créer le répertoire uploads s'il n'existe pas + $uploadDir = __DIR__ . '/uploads/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + // Générer un nom de fichier sécurisé + $fileName = uniqid('post_', true) . '.' . $validationResult['ext']; + $filePath = $uploadDir . $fileName; + + // Vérifier les permissions + if (is_writable($uploadDir)) { + // Déplacer le fichier + if (move_uploaded_file($file['tmp_name'], $filePath)) { + // Sauvegarder dans la DB + $stmt = $pdo->prepare( + "INSERT INTO `{$env['TABLE_FILES']}` + (id_message, nom_fichier, chemin_fichier, taille, type_mime, date_upload) + VALUES (?, ?, ?, ?, ?, NOW())" + ); + $stmt->execute([ + $postId, + $file['name'], + '/uploads/' . $fileName, + $file['size'], + $validationResult['mime'] + ]); + $message = 'Post publié avec succès !'; + } else { + $message = 'Erreur lors de l\'upload'; + } + } else { + $message = 'Erreur lors du téléchargement du fichier'; + } + } else { + $message = 'Le fichier contient du contenu dangereux'; + } + } else { + $message = $validationResult['error']; + } + } else { + $message = 'Post publié avec succès !'; + } + + // Enregistrer l'activité + $rateLimit->recordAttempt($userId, 'post'); + } + } + } +} + +// ======================== +// CHARGEMENT DES POSTS +// ======================== +$stmt = $pdo->query( + "SELECT m.*, u.pseudo, f.chemin_fichier, f.nom_fichier, f.taille + FROM `{$env['TABLE_MESSAGES']}` m + JOIN `{$env['TABLE_USERS']}` u ON m.id_utilisateur = u.id + LEFT JOIN `{$env['TABLE_FILES']}` f ON m.id = f.id_message + ORDER BY m.date_creation DESC" +); +$posts = $stmt->fetchAll(PDO::FETCH_ASSOC); + +// Si l'utilisateur est admin, charger le flag +$flag = null; +if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') { + $flag = $env['FLAG']; +} +?> + + +
+ + +🚀 Forum Équipe J
+Espace de discussion sécurisé
+