Description du problème
Depuis SPIP 4.4.17, lors de la création d’un compte via le formulaire natif :
#FORMULAIRE_INSCRIPTION{6forum}
l’inscription semble fonctionner correctement :
- l’auteur est créé dans
spip_auteurs; - son
loginest correctement généré ; - son statut est
nouveau; prefscontient bien6forum;- l’email d’inscription est envoyé ;
- cet email contient bien le mot de passe aléatoire généré.
En revanche, le champ pass de spip_auteurs reste vide.
Le compte créé ne peut donc pas se connecter avec le mot de passe reçu par email.
Versions testées
- SPIP 4.4.16 : fonctionnement OK
- SPIP 4.4.20 : problème reproductible
- La régression semble avoir été introduite en 4.4.17.
- PHP 8.4
- MySQL 8.x
Le problème est également reproductible sans le plugin Inscription 3, avec uniquement le formulaire natif #FORMULAIRE_INSCRIPTION{6forum}.
Diagnostic
Dans ecrire/action/inscrire_auteur.php, action_inscrire_auteur_dist() appelle :
$desc['pass'] = creer_pass_pour_auteur($desc['id_auteur']);
La fonction creer_pass_pour_auteur() génère correctement le mot de passe puis appelle :
auteur_instituer($id_auteur, ['pass' => $pass]);
Un traçage confirme que le mot de passe est bien généré :
PASS GENERE longueur=16
mais qu’après auteur_instituer() :
'id_auteur' => '6127',
'login' => 'nicolas_essai5',
'pass' => '',
'statut' => 'nouveau',
'source' => 'spip'
Cause identifiée
En comparant SPIP 4.4.16 et 4.4.20, la différence se trouve dans ecrire/action/editer_auteur.php.
En 4.4.16 :
if (isset($c['pass']) and strlen($c['pass'])) {
$champs['pass'] = $c['pass'];
}
Depuis le correctif de sécurité spip-security/securite#4894, auteur_instituer() vérifie maintenant l’autorisation :
if (
isset($c['pass'])
&& strlen($c['pass'])
&& autoriser('modifier', 'auteur', $id_auteur, null, ['pass' => '?'])
) {
$champs['pass'] = $c['pass'];
}
Lors d’une inscription publique, le visiteur n’est évidemment pas authentifié comme l’auteur qui vient d’être créé.
Le traçage donne donc :
isset=OUI
longueur=16
autoriser=NON
CHAMPS APRES AUTORISATION=
Par conséquent, $champs['pass'] n’est jamais défini et auth_modifier_pass() n’est jamais appelé.
auteur_instituer() retourne par ailleurs une chaîne vide, ce qui ne permet pas à creer_pass_pour_auteur() de détecter le problème.
Comparaison avec inscription_nouveau()
Le problème semble d’autant plus identifiable que inscription_nouveau() gère déjà ce cas en levant temporairement l’autorisation :
autoriser_exception('modifier', 'auteur', $id_auteur);
auteur_modifier($id_auteur, $desc);
autoriser_exception('modifier', 'auteur', $id_auteur, false);
Mais creer_pass_pour_auteur() ne fait pas de même avant :
auteur_instituer($id_auteur, ['pass' => $pass]);
Test d’un correctif
Le problème disparaît en appliquant le même principe dans creer_pass_pour_auteur() :
function creer_pass_pour_auteur($id_auteur) {
include_spip('inc/acces');
$pass = creer_pass_aleatoire(max(_PASS_LONGUEUR_MINI, 16), $id_auteur);
include_spip('inc/autoriser');
include_spip('action/editer_auteur');
autoriser_exception('modifier', 'auteur', $id_auteur);
auteur_instituer($id_auteur, ['pass' => $pass]);
autoriser_exception('modifier', 'auteur', $id_auteur, false);
return $pass;
}
Après cette modification, le champ pass contient bien le hash attendu dans spip_auteurs.
Remarque
Une logique similaire semble déjà avoir été appliquée dans prive/formulaires/mot_de_passe.php, où une autoriser_exception('modifier', 'auteur', $id_auteur) est utilisée avant la modification du mot de passe.
Il semble donc que le renforcement des autorisations de modification de login / pass ait corrigé un problème de sécurité, mais que le cas de creer_pass_pour_auteur() lors d’une inscription publique n’ait pas été adapté en conséquence.
Résultat attendu
Après :
#FORMULAIRE_INSCRIPTION{6forum}
le mot de passe aléatoire envoyé à l’utilisateur doit être enregistré/hashé dans spip_auteurs.pass, comme c’était le cas en SPIP 4.4.16.
Résultat actuel
L’utilisateur reçoit bien son mot de passe par email, mais spip_auteurs.pass reste vide et la connexion est impossible.