Le WordPress d'aujourd'hui, décodé pour les développeurs

Sécurité

Vérifier la provenance d’un fichier de traduction avant de le charger avec load_textdomain

Un fichier .mo ou .po fourni par un contributeur externe peut transporter du contenu inattendu. Voici comment le valider avant chargement.

Par WordPress Développement • 19 juillet 2023 • 4 min de lecture • Aucun commentaire
Vérifier la provenance d'un fichier de traduction avant de le charger avec load_textdomain

Comment s’assurer qu’un fichier de traduction envoyé par un traducteur indépendant, un partenaire ou un contributeur communautaire ne contient rien d’autre que des chaînes traduites avant de l’intégrer avec load_textdomain() ? La question se pose dès qu’un projet accepte des traductions externes au format .po/.mo, une pratique courante pour des extensions ou des thèmes destinés à plusieurs marchés linguistiques.

Le format .mo, compilé et binaire, ne s’inspecte pas à l’œil comme un fichier texte. Il peut techniquement contenir des chaînes très longues, des métadonnées inattendues dans son en-tête, ou avoir été généré par un outil qui ne respecte pas strictement la spécification GNU gettext. Rien de tout cela ne constitue une exécution de code à proprement parler — load_textdomain() ne fait qu’analyser un format de données — mais un fichier corrompu ou trafiqué peut provoquer un comportement inattendu, voire un plantage, s’il est chargé sans contrôle.

Étape 1 : ne jamais accepter un fichier de traduction par upload direct côté public

La première règle, souvent négligée, consiste à ne jamais exposer un point d’entrée permettant à un visiteur non authentifié de déposer un fichier .mo qui sera ensuite chargé automatiquement. Si un mécanisme de contribution de traductions existe sur un projet, il doit passer par un espace d’administration restreint, avec une capacité dédiée, jamais par un formulaire public.

Étape 2 : valider l’extension et la structure avant tout traitement

Avant d’appeler load_textdomain(), un contrôle minimal sur le fichier reçu s’impose :

function traduction_est_valide( string $chemin ): bool {
    if ( ! file_exists( $chemin ) || ! is_readable( $chemin ) ) {
        return false;
    }
    if ( pathinfo( $chemin, PATHINFO_EXTENSION ) !== 'mo' ) {
        return false;
    }
    // Un fichier .mo commence par un « magic number » précis.
    $entete = file_get_contents( $chemin, false, null, 0, 4 );
    $signatures_valides = array( "\xde\x12\x04\x95", "\x95\x04\x12\xde" );
    foreach ( $signatures_valides as $signature ) {
        if ( $entete === $signature ) {
            return true;
        }
    }
    return false;
}

Ce contrôle ne remplace pas une revue humaine du contenu traduit, mais il élimine immédiatement les fichiers mal formés ou déguisés sous une autre extension.

Étape 3 : charger depuis un répertoire dédié, jamais depuis un chemin arbitraire

L'essentiel à retenir : Un fichier de traduction reste un fichier binaire à valider comme les autres ; Vérifier l'extension, la taille et l'origine avant chargement ; Charger depuis un répertoire dédié, jamais depuis un upload direct

Le chemin transmis à load_textdomain() ne doit jamais provenir directement d’une valeur saisie par l’utilisateur ou extraite d’un nom de fichier téléversé sans normalisation. La bonne pratique consiste à stocker les fichiers acceptés dans un répertoire dédié, hors du webroot public accessible directement, puis à reconstruire le chemin à partir d’un identifiant contrôlé côté serveur :

$langue = sanitize_key( $_POST['langue'] ?? '' );
$chemin = WP_CONTENT_DIR . '/traductions-externes/' . $langue . '.mo';

if ( traduction_est_valide( $chemin ) ) {
    load_textdomain( 'mon-plugin', $chemin );
}

sanitize_key() réduit la valeur à des caractères alphanumériques et tirets, ce qui empêche une tentative de traversée de répertoire via des séquences comme ../.

Étape 4 : consigner la provenance de chaque fichier accepté

Sur un projet qui accepte des traductions de plusieurs contributeurs, garder une trace de qui a fourni quel fichier, à quelle date, facilite l’investigation en cas de problème constaté a posteriori. Un simple journal, même sous la forme d’un fichier CSV horodaté dans un répertoire non public, suffit pour la majorité des projets :

  • Nom du contributeur ou de la source
  • Date de réception
  • Somme de contrôle (hash_file( 'sha256', $chemin )) du fichier accepté
  • Date d’activation effective sur le site

Étape 5 : tester le fichier sur un environnement isolé avant mise en production

Avant d’activer une nouvelle traduction sur le site principal, la charger sur un environnement de recette permet de vérifier deux choses : que le fichier s’interprète sans erreur PHP, et que les chaînes traduites correspondent à l’original sans ajout suspect (lien inattendu, formulation hors sujet). Cette vérification manuelle reste la meilleure protection contre un contenu traduit qui s’écarterait du texte source sans que cela ne soit techniquement une erreur de format.

Un fichier de traduction n’est jamais « juste du texte » : c’est un fichier qui sera interprété par le cœur de WordPress, et qui mérite le même niveau de vigilance qu’un fichier de configuration.

Pour aller plus loin

Ces vérifications restent pertinentes indépendamment de l’évolution future du format de traduction de WordPress ; à la date de cet article, le format historique .mo/.po reste la seule norme en production, et aucune alternative n’est encore disponible dans le cœur. La documentation de référence sur l’internationalisation reste consultable sur developer.wordpress.org, pour toute évolution ultérieure du mécanisme de chargement.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi