Sécurité
- Revue de sécurité de tout le code, et correction des failles graves qu'elle a relevées :
- Sauvegardes : les archives de sauvegarde (copie de la base et du dossier
config/, qui contient les mots de passe) ne sont plus téléchargeables par une simple adresse web, sous Apache comme sous nginx ; seule l'administration (Monitoring) les remet. Leur nom contient désormais une partie aléatoire, impossible à deviner. Le dossier des journaux (logs/) est protégé de la même façon. - Jetons secrets : identifiants de session, jetons des formulaires et liens envoyés par e-mail (mot de passe oublié, validation du compte) sont tirés d'un générateur aléatoire sûr ; ils étaient prévisibles. Ces liens expirent au bout d'une heure, et une nouvelle demande annule la précédente.
- Connexion : la double authentification (2FA) et le bannissement sont vérifiés quelle que soit la façon de se connecter — mot de passe, compte Discord, Google ou GitHub, lien « mot de passe oublié » —, et plus seulement avec le mot de passe.
- Liens dans les e-mails : l'adresse du site utilisée dans les liens envoyés par e-mail, au retour d'une connexion
par Discord, Google ou GitHub et au retour d'un paiement Stripe est celle enregistrée à l'installation
(
config/url.php), et non plus celle que la requête annonce : un attaquant ne peut plus faire envoyer un lien qui mène chez lui. - Pages d'administration : un nom de pièce jointe piégé (dans un signalement de modération) ou une fausse adresse
IP (en-tête
X-Real-IP, dans l'historique des sessions) pouvaient y injecter du code ; ils sont vérifiés et neutralisés. Le message d'accueil d'un serveur de jeu, fourni par un service extérieur, est nettoyé avant affichage. - Actions d'administration : supprimer, activer ou désactiver, fermer, approuver, restaurer, purger… exigent désormais un jeton de sécurité dans les 23 modules qui n'en avaient pas. C'étaient de simples liens ou des formulaires sans jeton : un lien piégé, cliqué par un administrateur connecté, suffisait à déclencher l'action.
- Outils : les scripts du dossier
tools/ne s'exécutent qu'en ligne de commande, jamais depuis une adresse web ; à chaque modification, l'intégration continue vérifie qu'aucune dépendance PHP n'a de faille connue (composer audit). - Boutons rendus muets par la politique de sécurité (CSP) : la CSP stricte du site bloque les actions écrites dans
le HTML (
onclick="…"), si bien que ces boutons ne faisaient rien : bandeau des cookies, nombre de lignes des tableaux d'administration, confirmations de la corbeille et des révisions, nom du fichier joint dans la messagerie, actions groupées des mentions du forum, sélection d'un champ au clic, bouton de la fenêtre de suppression. Ils sont rebranchés autrement et fonctionnent aussi dans le contenu chargé sans recharger la page. Le choix du nombre de lignes appelait en outre jQuery, pourtant retiré.
- Sauvegardes : les archives de sauvegarde (copie de la base et du dossier
- Politique de sécurité du contenu (CSP) stricte : servie par le site avec un jeton propre à chaque page, elle
refuse les scripts écrits dans la page sans ce jeton (
unsafe-inline), le code construit à la volée (unsafe-eval) et les objets intégrés (object-src 'none') ; les scripts ne viennent plus que du site et d'une courte liste d'adresses, TinyMCE et CodeMirror étant désormais hébergés par le site. - Session renouvelée à la connexion : l'identifiant de session change quand on se connecte ; un identifiant imposé avant la connexion ne donne plus accès à la session ouverte.
- Secrets chiffrés en base : le mot de passe SMTP et la clé de la double authentification (2FA) sont chiffrés (AES-256-GCM) ; une copie de la base ne les livre plus en clair. Une valeur existante est chiffrée au prochain enregistrement.
- Webhooks : un webhook ne peut plus viser le réseau interne du serveur (adresses privées, locales ou réservées refusées, redirections non suivies).
Ajouté
- Fichiers CSS et JavaScript toujours à jour : chaque fichier est appelé avec sa date de modification (
?v=…), si bien que le navigateur le recharge dès qu'il change — y compris un fichier remplacé par un thème ou envoyé à la main —, sans avoir à augmenternf_version_css(qui reste utilisé quand le fichier est introuvable). - Marketplace : la fenêtre « Mises à jour » de la page Thèmes & Addons signale qu'une nouvelle version de NeoFrag Reborn est disponible ; la mise à jour du cœur elle-même reste manuelle.
- Mises à jour des addons : la même fenêtre compare les addons installés au catalogue du marketplace et installe les nouvelles versions, avec leurs migrations de base de données.
- Installeur : nouvelle présentation — logo NeoFrag Reborn, bandeau « Fork non officiel de NeoFrag », crédit à Michaël BILCOT et Jérémy VALENTIN (licence LGPLv3) en pied de page, polices Inter et Space Grotesk, couleurs turquoise.
- Fichiers : un gestionnaire de fichiers (arborescence de dossiers, envoi, droits de lecture par fichier ou par dossier), porté de HiddenCMS, de HiddenBlob (LGPLv3). Livré avec le cœur.
- Émojis personnalisés (module Emojis, livré avec le cœur) : l'administration ajoute des images nommées, que
:nom:affiche dans les messages et signatures du forum, les actualités, les pages, les événements et le recrutement, entre autres. - Newsletter : envoi programmé à la date choisie, par petits lots traités par la tâche planifiée du site (cron) au lieu de tout envoyer d'un coup ; suivi des ouvertures (taux d'ouverture par campagne, mesuré par une image invisible) ; modèles d'e-mail réutilisables ; choix des destinataires : tous les abonnés, les membres seulement ou un groupe.
- Événements : un événement peut se répéter chaque jour, chaque semaine ou chaque mois, pour le nombre de fois choisi — chaque date devient un événement à part entière. Les participants, sauf ceux qui ont décliné, reçoivent un rappel sur le site (cloche des notifications) avant l'événement, 24 heures avant par défaut ; les rappels passent par la tâche planifiée du site (cron).
- Réactions par émoji : six réactions au choix (👍❤️😂😮😢😡) au lieu d'un seul cœur, comme sur Discord ou Facebook, avec le compte de chacune ; les anciens « j'aime » deviennent des ❤️.
- Widget « Statut live » (l'ancien widget Twitch) : suit plusieurs chaînes à la fois, sur Twitch et sur YouTube (identifiants Twitch et clé d'API YouTube à fournir), avec jeu, spectateurs, titre, miniature et lecteur intégré.
- Pages composées : sous son texte, une page peut afficher des blocs d'autres modules (dernières actualités,
actualités d'une catégorie, derniers articles, téléchargements populaires), placés dans l'ordre voulu et réglés un à
un depuis la modification de la page. Le code court accepte aussi des réglages, par exemple
[block:news.latest count=2]. - Thème Extend, de Chewbaka, porté sur NeoFrag Reborn (Bootstrap 5, modes jour et nuit), sous licence CC BY-NC-SA 4.0 ; disponible dans le marketplace.
- Recherche instantanée : le widget de recherche propose des résultats dès la deuxième lettre (actualités, forum, pages).
- Tri des listes d'administration : dix listes (actualités, articles, petites annonces, téléchargements, FAQ, livre d'or, liens, médias, sondages, wiki) se trient selon le critère choisi.
- Avatars animés : un GIF animé envoyé comme avatar reste animé — toujours si le serveur dispose d'Imagick, sinon tant qu'il ne dépasse pas 250 × 250 pixels.
- Permissions : la grille des permissions s'ouvre dans une fenêtre, sans quitter la page.
- Gestionnaire de fichiers du webmaster (Monitoring, bouton « Gérer les fichiers ») : parcourir les fichiers du
site, les modifier dans un éditeur (CodeMirror), en créer, en renommer ou en supprimer. Les dossiers
config/,logs/etbackups/restent hors d'atteinte ; chaque enregistrement garde une copie.nfbaket entre au journal d'audit. Toute écriture exige le mot de passe webmaster, distinct de celui du compte et gardé hors de la base (défini à l'installation ou dans Monitoring, carte « Sécurité webmaster ») ; une fois défini, il est aussi redemandé pour supprimer ou purger des sauvegardes et pour changer la clé de la tâche planifiée. - Notes de version tirées d'une seule source :
tools/changelog-section.phpextrait une version de ce journal (CHANGELOG.md), en Markdown ou en HTML ; les notes des releases GitHub en sont tirées, et leurs textes ne peuvent plus diverger.
Modifié
- Installeur « tout compris » : l'assistant passe à quatre étapes (Prérequis → Base de données → Administrateur → Terminé). Tous les modules, widgets et thèmes livrés sont installés et activés à l'étape « Base de données », et la page d'accueil affiche les actualités dès la fin de l'installation ; l'étape « Modules » et ses profils disparaissent.
- Bootstrap 4.6.2 → 5.3.8, et jQuery n'est plus chargé : le JavaScript du site s'appuie sur un petit outil maison
(
window.NF). Neuf extensions jQuery ou Bootstrap 4 sont remplacées (notifications → toasts de Bootstrap 5, selectize → Tom Select, sélecteur de date → flatpickr, FullCalendar 3 → 6, sélecteurs de couleur et d'icône, arborescence et jauges → code maison, barres de défilement → défilement natif), et jQuery UI par SortableJS. Quatre scripts oubliés appelaient encore jQuery : ils sont réécrits en 1.2.0. - Mode sombre : tableaux, cartes, menus déroulants et éditeur de texte prennent les couleurs sombres du thème au lieu de rester blancs, dans tous les thèmes.
- Réglages des widgets et disposition des pages : enregistrés en JSON au lieu du format de sérialisation de PHP, ce qui ferme une voie d'injection de code ; les anciens réglages restent lus.
- Graphiques des statistiques : Chart.js (licence MIT) remplace Highstock, une bibliothèque commerciale qui interdisait de redistribuer librement le CMS.
Corrigé
- MySQL 8 : ouvrir un sujet ou répondre sur le forum, scinder ou fusionner des sujets, créer une conversation dans la messagerie, réordonner les images du Slider… échouaient par une erreur fatale sur MySQL 8, qui refusait la façon dont le site ouvrait ses transactions. Elles passent désormais par les fonctions prévues de mysqli.
- Émojis : un émoji — ou tout caractère codé sur 4 octets — dans un texte provoquait une erreur de base de données.
La connexion et toutes les tables passent en
utf8mb4(interclassementutf8mb4_unicode_ci, reconnu par MySQL 8 comme par MariaDB 10 ; les interclassements propres à MariaDB 11, qui faisaient échouer l'installation ailleurs, disparaissent). where('col', [])générait une condition vide (→DELETE/UPDATEsur toute la table) : produit désormais1 = 0(ensemble vide).- Dates au-delà de 2038 : l'envoi programmé d'une newsletter, la publication programmée d'une actualité, d'un article, d'une galerie ou d'une page, et les dates d'un événement ne pouvaient pas dépasser le 19 janvier 2038 : au-delà, MariaDB refusait l'enregistrement, sans aucun message. Ces dates vont désormais jusqu'en l'an 9999 ; les installations existantes sont mises à jour par migration.
/user/loginet/user/registrationrépondaient « page introuvable » : les boutons « Connexion » et « Inscription » des thèmes y mènent quand JavaScript est désactivé ou que le lien s'ouvre dans un nouvel onglet. Ces adresses ouvrent désormais la fenêtre de connexion ou d'inscription.- Nebula : le bouton « Inscription » restait affiché quand les inscriptions étaient fermées, et menait à une page introuvable. Il est masqué dans ce cas.
- Widget Discord : il donne la vraie cause d'un échec (widget désactivé sur le serveur, identifiant introuvable, réseau) au lieu d'un message général, montre le vrai logo du serveur (lu dans l'invitation publique) et compte juste les membres en ligne — la liste fournie par Discord s'arrête à 100, ce qui faussait le compte des grands serveurs.
- Widget TeamSpeak, affichage en arbre : il plantait sous PHP 8 et retombait sur la carte simple. L'arbre des canaux et des clients est désormais dessiné par le widget lui-même, avec des icônes Font Awesome (plus besoin de pack d'icônes) ; une erreur réseau affiche un message général, sans révéler l'adresse ni le port du serveur.
- Widgets Discord, Twitch et Serveur de jeu : ils affichaient « inaccessible » même quand le service répondait (la réponse était décodée deux fois). Les requêtes du site envoient aussi un identifiant de navigateur (User-Agent) : sans lui, les services protégés par Cloudflare répondaient par un refus (erreur 403).
- Sauvegardes et Monitoring : créer une sauvegarde plantait sur les serveurs en PHP-FPM, et échouait sous nginx ou
Plesk, qui répondaient « page introuvable » aux adresses en
.json; supprimer ou télécharger une sauvegarde menait à une page introuvable ; l'arborescence du Monitoring affichait une erreur au premier rafraîchissement. Tout est corrigé. - Marketplace injoignable : URL par défaut passée en non-www (
https://neofrag-reborn.xyz/marketplace). - Envoi de plusieurs fichiers : le premier fichier d'un envoi multiple faisait échouer l'envoi
(« page introuvable ») — c'est ce qui bloquait l'envoi dans le module Fichiers. Pour les développeurs :
uploaded_file()prenait l'indice0pour une absence d'indice. - Webhooks : l'inscription d'un membre (
user.registered), un nouveau commentaire (comment.created) et un nouveau sujet du forum (forum.topic) déclenchent enfin leur webhook ; ces événements étaient proposés mais jamais émis.
Documentation
- Documentation (guides du wiki) : pour les développeurs, les formulaires (
form()etform2()) et la validation des réglages d'un widget ; pour les administrateurs, le dépannage de l'installation, un exemple de création de rôle et que faire quand le marketplace est injoignable. Une note explique comment régler nginx ou Plesk, qui répondaient « page introuvable » aux adresses en.json.