Skip to content

V1.1

Choose a tag to compare

@Assistouest Assistouest released this 25 Mar 09:39
· 21 commits to main since this release

Refactorisation complète de installer.php

Contexte

installer.php est un script PHP statique, versionné en SVN, qui s'exécute entièrement en dehors du bootstrap WordPress. Il est intentionnellement accessible via HTTP et prend en charge le pipeline complet d'import une fois que class-importer::step_prepare() a écrit installer_config.json dans le répertoire de session protégé.

Ce commit documente et consolide l'architecture établie en v1.1.0.
Aucune régression fonctionnelle — tous les comportements existants sont préservés.


Modèle de sécurité

Quatre couches de défense appliquées dans un ordre strict, avant toute opération sensible.

1. Verrou de présence de config → HTTP 404

file_exists(installer_config.json) est vérifié avant les en-têtes CORS, avant l'authentification, avant tout traitement. En l'absence du fichier → 404 + exit. L'installeur est inerte par défaut et à tout moment en dehors d'une fenêtre d'import active de 15 minutes.

2. Expiration du TTL → HTTP 410 + auto-destruction

expires_at est vérifié immédiatement après le chargement de la config. TTL expiré → wpcm_self_destruct() supprime installer_config.json et retourne HTTP 410. La fenêtre de 15 minutes est intentionnellement courte.

3. Liste blanche d'origine CORS → HTTP 403

allowed_origin est gravé au moment de la génération par step_prepare() à partir de admin_url() — il n'est jamais reflété depuis la requête. Les clients non-navigateur (cURL, WP-Cron) qui n'envoient pas d'en-tête Origin sont autorisés à passer.

4. Authentification bcrypt à usage unique → HTTP 410 + auto-destruction

Le token client est généré par crypto.getRandomValues() dans le navigateur et envoyé une seule fois à step_prepare(). Le plugin WP ne stocke que bcrypt_hash(token) dans installer_config.json — le token brut n'est jamais stocké ni retourné. password_verify() est le seul chemin d'entrée valide. Un token incorrect déclenche immédiatement wpcm_self_destruct(), donnant à un attaquant exactement une tentative avant que l'installeur devienne définitivement inerte.


Chiffrement des identifiants DB

Les identifiants de base de données sont chiffrés dans class-importer::step_prepare() avec AES-256-CBC.

  • Clé : SHA-256(client_token) — 32 octets dérivés du token bcrypt vérifié
  • IV : random_bytes(16)
  • Stockage : base64(IV || ciphertext) dans installer_config.json

Le hash bcrypt et le blob AES sont inutilisables sans le token client original, que seul le navigateur détient.


Pipeline d'import — 4 étapes, toutes reprenables

database — Import SQL découpé dans le temps

  • Budget de 8 secondes par appel AJAX, compatible avec les timeouts FastCGI et hébergement mutualisé (30 s).
  • Suppression préalable de toutes les tables de destination au premier appel (batch DROP TABLE IF EXISTS).
  • Reprise à la borne de l'instruction : l'offset en octets est sauvegardé immédiatement après chaque ; exécuté, évitant la réexécution infinie sur une même ligne multi-instructions.
  • Conscience de max_allowed_packet : les INSERT … VALUES trop volumineux sont découpés en sous-chunks à la frontière de chaque ligne de données.
  • Récupération sur clé dupliquée : réessai automatique en REPLACE INTO sur errno 1062.
  • Chien de garde de connexion : reconnexion automatique sur gone away / lost connection.
  • Mode maintenance (fichier .maintenance) activé à l'entrée de l'étape et rafraîchi à chaque appel pour que les longs imports ne fassent jamais expirer la fenêtre.

files — Restauration complète de wp-content

  • Nettoyage intégral de wp-content avant restauration.
  • Préservés : dossier clone-master, wpcm-temp (session active), wpcm-backups, wpcm-logs.
  • Protection ZIP Slip : chaque entrée d'archive est normalisée et vérifiée pour rester dans le répertoire d'extraction temporaire avant tout appel à ZipArchive::extractTo().
  • Restaure : thèmes, plugins, uploads (par année/mois), mu-plugins, langues, drop-ins et fichiers de config racine (.htaccess, robots.txt).

replace_urls — Remplacement serialization-safe découpé dans le temps

  • Budget de 3 secondes par appel AJAX.
  • Paires d'URLs couvrant : variantes https/http, protocole-relatif (//domain), JSON-échappé (\/), URL-encodé et chemins absolus serveur.
  • Gestion des données PHP sérialisées (correction des longueurs d'octets après remplacement), blobs JSON, sérialisé-dans-JSON imbriqué, et fallback __PHP_Incomplete_Class via remplacement de chaîne brut.
  • Troncature des tables à index UNIQUE connus (Yoast, Redirection) pour éviter les erreurs de clé dupliquée.
  • Résolution errno 1406 (données trop longues) par fallback colonne par colonne, et errno 1062 (clé unique dupliquée) par suppression de la ligne conflictuelle avant une nouvelle tentative.

finalize — Nettoyage et durcissement post-migration

  • Purge des transients, rewrite_rules, auth_key, cache CSS Elementor, fichiers cache WP Rocket et W3TC.
  • Désactivation automatique des plugins de sécurité/WAF connus pour dysfonctionner sur un nouveau domaine (Wordfence, Sucuri, iThemes Security, Jetpack, plugins reCAPTCHA, etc.) — la liste est sauvegardée dans wpcm_deactivated_plugins pour affichage en notice admin.
  • Neutralisation des entrées auto_prepend_file de Wordfence WAF dans .user.ini, php.ini et .htaccess.
  • Application des import_opts : changement de locale, réinitialisation des permaliens, blocage d'indexation (SQL + flag wpcm_noindex + MU-plugin auto-supprimant).
  • Garantit que clone-master lui-même reste actif après l'import.
  • Supprime le répertoire de session (y compris installer_config.json) → l'installeur devient définitivement inerte.
  • Lève le mode maintenance.

Filet de sécurité sur erreur fatale

register_shutdown_function() intercepte E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR (non rattrapables par try/catch).

  • Erreur fatale non-timeoutwpcm_self_destruct() se déclenche, réponse JSON propre.
  • Timeout PHP → l'installeur reste actif, la boucle de polling JS peut reprendre depuis le dernier offset octet/ligne sauvegardé.

Fichiers modifiés

Fichier Détail
installer.php Refactorisation complète — 1 664 lignes, 18 fonctions, 4 étapes d'import, 8 blocs phpcs:disable avec justifications de contexte standalone