SPIP 4.1

Changements apportés par SPIP 4.1

SPIP 4.1 est avant tout une version qui suit les versions maintenues de PHP (7.4 à 8.1) et met à jour différentes librairies utilisées en interne. Il modifie aussi le système d’authentification des auteurs.

Compatibilité PHP

SPIP 4.1 nécessite PHP 7.4 minimum, et fonctionne jusqu’à PHP 8.1.
Il nécessite les extensions PHP : sodium, Zlib, Zip et Phar.

Rappel :  SPIP 4.0 c’est PHP 7.3 à 8.0.

Compatibilité PHP 8.1

Pour la compatibilité PHP 8.1, nous avons du corriger / adapter de nombreuses fonctions.

Cela concerne particulièrement des appels à des fonctions de PHP traitant des chaînes de caractères, si elles reçoivent null au lieu d’une chaîne, pour éviter des deprecated : https://php.watch/versions/8.1/internal-func-non-nullable-null-deprecation

Authentification & Chiffrement

Nous reprenons en l’adaptant le travail de g0uz sur le plugin Chiffrer.

-  Cela corrige certains rares cas où la librairie Javascript utilisée jusqu’à présent échouait à identifier l’utilisateur
-  Cela renforce la sécurité en évitant certains types d’attaques.

Le formulaire de login

HTTPS est désormais encore plus fortement conseillé

En effet, SPIP ne cherche plus à chiffrer le mot de passe des auteurs en Javascript depuis le formulaire de login, et le mot de passe arrive donc systématiquement « en clair » à SPIP. Il est donc fortement conseillé d’avoir son site en HTTPS, car HTTPS assure alors la sécurité de la transmission.

En conséquence, la constante _AUTORISER_AUTH_FAIBLE, qui permettait de forcer l’envoi du mot de passe en clair, est supprimée.

Poivre, sel, hash du mot de passe

Le mot de passe d’un auteur est maintenant haché dans SPIP en utilisant un poivre (une clé spécifique pour le site). Par ailleurs, la fonction password_hash de PHP s’occupe de trouver le meilleur algorithme de hachage et de créer un sel dedans. La fonction password_verify de PHP est utilisée pour le vérifier.

Les "aléas" rotatifs des auteurs sont toujours créés, mais ne sont plus utilisés pour saler le mot de passe vu que PHP gère cela très bien. Par contre, l’application supplémentaire d’un poivre est une nouveauté.

Comme avant, à chaque authentification réussie d’un auteur, on enregistre un nouveau hash en base, en recréant un hash avec password_hash : cela applique un nouveau sel dessus donc, et éventuellement PHP en profite pour utiliser un algorithme de hachage plus performant si besoin.

Clés de chiffrement

Pour mener à bien ce poivre, et d’autres actions dans SPIP, des clés de chiffrement sont utilisées et stockées dans config/cles.php. Il y en a 2 par défaut :

  • secret_des_auth : elle sert à poivrer le mot de passe des auteurs. C’est une nouvelle clé donc.
  • secret_du_site : elle sert pour moitié à calculer le secret du site. L’autre moitié étant stocké en base dans l’entrée secret_du_site de la table spip_meta. Ce secret du site (combiné) permet de signer et/ou chiffrer certaines parties de SPIP (contextes ajax, actions d’auteurs).

Lorsqu’un webmestre se connecte, les clés sont sauvegardées dans le nouveau champ backup_cles de la table spip_auteurs, en le chiffrant avec le mot de passe en clair de l’auteur.

Ceci permet de restaurer les clés du site si le fichier cles.php a été effacé, lors de l’authentification d’un webmestre.

La classe \Spip\Chiffrer\SpipCles permet de manipuler les clés au besoin.

Password

SPIP fournit deux méthodes qui sont utilisées dans ecrire/auth/spip.php

-  \Spip\Chiffrer\Password::verifier($clair, $hash, $key = null): bool
-  \Spip\Chiffrer\Password::hacher($clair, $key = null): string

La clé par défaut est celle de secret_des_auth, utilisée pour poivrer.

Chiffrement

Trois méthodes sont aussi fournies pour chiffrer et déchiffrer (chiffrage symétrique) des contenus.
Dedans nous utilisons la librairie Sodium (fournie par défaut dans PHP >= 7.2).

-  Chiffrement::chiffrer($message, $key): ?string
-  Chiffrement::dechiffrer($encoded, $key): ?string
-  Chiffrement::keygen(): string

La méthode keygen produit simplement une clé de chiffrement à la longueur adaptée.

Les méthodes chiffrer() et dechiffrer() seront très lentes si la clé transmise n’a pas la bonne longueur : on considère alors la clé de chiffrement comme étant un mot de passe nécessitant un traitement supplémentaire lourd (avec sodium_crypto_pwhash).

Signature des actions

Les actions sont authentifiées maintenant en utilisant hash_hmac et hash_equals (et les aléas d’auteurs et/ou le secret du site)

Qualité du code

Il y a de quoi progresser sur la qualité du code PHP de SPIP… l’avenir ne peut être que meilleur ;-)

Typage

Ce faisant pour PHP 8.1, nous avons commencé à typer certains arguments et retours de fonctions pour détecter au plus tôt certains appels incorrects.

C’est un changement potentiellement impactant : cela peut créer des erreurs de squelettes là où l’erreur était auparavant plus silencieuse ou tolérée. De même cela pourrait provoquer des erreurs PHP dans des plugins ou des scripts maison sur les appels erronés à ces fonctions.

En assurant la compatibilité avec les nouvelles versions de PHP nous irons forcément vers plus de typage dans le code de SPIP au fur et à mesure des évolutions et factorisations du code source.

La conséquence sera donc que les personnes développant des plugins ou des squelettes devront être plus vigilantes sur les appels effectués d’une part, et d’autre part que nous restreindrons certainement les nouvelles fonctions à des types uniques (éviter les mixed), de même que certaines fonctions déjà existantes verront probablement certains types d’arguments mixtes réduits (éviter qu’un argument puisse recevoir un peu tout comme int | string | array par exemple), de même pour les retours de fonctions.

Au vu du code de SPIP historique ce ne sera pas si évident.

Sucre syntaxique

Nous avons passé l’outil Rector sur le code de SPIP  4.1 avec la configuration PHP 7.4. L’outil permet de transformer la syntaxe du code source PHP en modifiant certains éléments qui peuvent être simplifiés grâce à des écritures plus récentes de PHP. Par exemple utiliser les opérateurs ?? ou ??= lorsque c’est possible (Voir la doc PHP).

C’est toujours ça de pris :-)

Changements d’API sur les URLs et objets

Certaines fonctions d’appels pour calculer ou décoder les URLs sont modifiées.
Les anciennes API sont dépréciées.

Il y a maintenant 2 jeux de fonctions distincts pour générer l’URL d’un objet et pour décoder une URL. Ce nouveau mécanisme permet de définir une fonction pour générer une URL propre pour les #URL_PAGE.

Les nouvelles fonctions sont typées. Dans les explications suivantes, on ne présente que le début des signatures des différentes fonctions modifiées.

Voir la demande d’ajout correspondante pour le détail complet.

Encodage

generer_objet_url (anciennement generer_url_entite)

Cette fonction génére une URL, qui peut être soit pour l’espace public (paramètre $public = true), soit pour l’espace privé ($public = false), soit par défaut ($public = null) retourner une URL pour l’espace dans lequel on se trouve actuellement …

La fonction generer_objet_url() est la fonction de haut niveau, qui va finir par appeler une fonction urls_xxx_generer_url_objet()_dist selon le module d’URL actif.

Note : Pour cette fonction la signature change légèrement.

Dans generer_url_entite() maintenant déprécié :

  • Le 5e paramètre $public pouvait valoir true, false, null ou 'string'. Dans le cas d’une chaine, il correspondait à un paramètre "connect".

Ce sont maintenant 2 paramètres séparés pour generer_objet_url :

  • Le 5e est ?bool $public = null (donc true, false ou null),
  • le 6e est string $connect = ''.

Dans la majorité des cas, cela n’affectera pas les usages et renommer simplement la fonction devrait être suffisant.

- generer_url_entite($id, $objet, ...)
+ generer_objet_url($id, string $objet, ...)

generer_objet_url_absolue

Generer l’url absolue vers un objet

- generer_url_entite_absolue($id, $entite, ...)
+ generer_objet_url_absolue($id = 0, string $entite = '', ...)

Décodage

La fonction de décodage des URLs s’appelle maintenant urls_xxxx_decoder_url_dist() ce qui évite toute confusion avec l’ancienne fonction urls_xxxx_dist().

De plus, pour un module d’URLs qui voudrait être compatible avec les anciennes versions de SPIP et cette nouvelle convention, il suffit de proposer une fonction urls_xxxx_dist() non typée qui fait le routage vers les 2 fonctions urls_xxx_generer_url_objet_dist() et decoder_url() du module.

Objet

generer_objet_lien

Génère un lien (titre cliquable vers url) vers un objet

- generer_lien_entite($id_objet, $objet, ...)
+ generer_objet_lien(int $id_objet, string $objet, ...)

generer_objet_info

Donner une information sur un objet éditorial. Utilisé entre autres par #INFO_XX

- generer_info_entite($id_objet, $type_objet, $info, ...)
+ generer_objet_info($id_objet, string $type_objet, string $info, ...)

Les fonctions permettant de définir des calculs spécifiques pour l’obtention d’information sont aussi renommées. Pour l’information calculée ’xxx’ : appelé via generer_objet_info(3, 'article', 'xxx') ou #INFO_XXX{article,3}.

Si ces fonctions existent, elles sont utilisées par SPIP dans ce cas :

// xxx spécifiquement sur l’objet 'type'
- generer_xxx_{type}($id_objet, $type_objet, ...)
+ generer_{type}_xxx($id_objet, string $type_objet, ...)

// sinon xxx sur un objet quelconque
- generer_xxx_entite($id_objet, $type_objet, $row, ...)
+ generer_objet_xxx($id_objet, string $type_objet, array $row, ...)

L’ancien nommage est déprécié.

Suivant ce principe, la fonction interne à SPIP pour calculer l’introduction a été renommée :

generer_objet_introduction

Génère l’introduction de l’objet (fonction privée à SPIP), utilisé par #INTRODUCTION et #INFO_INTRODUCTION)

- generer_introduction_entite($id_objet, $objet, $ligne_sql, ...)
+ generer_objet_introduction(int $id_objet, string $objet, array $ligne_sql, ...)

Réécriture de transmettre en .api

Pour faciliter et sécuriser partiellement l’envoi de données spécifiques (CSV, JSON) à des auteurs connus de SPIP, l’URL d’api transmettre.api a été mis en place (elle remplace un ancien mécanisme à l’objectif identique qui utilisait un squelette transmettre.html).

L’URL est générée avec la fonction generer_url_api_low_sec() (« low sec » signifiant ici « sécurité basse »). Un jeton fixe est créé pour cette action (avec ses paramètres) pour l’auteur désigné et sera acceptée même si l’auteur est déconnecté (ce qui veut dire que quiconque possède l’URL pourra accéder au contenu renvoyé).

Historiquement cela permet de générer des fichiers non publics (RSS des articles proposés à publication, statistiques du site) qui peuvent être exploités par des outils tierces (agrégateur RSS par exemple).

Également :

  • la fonction securiser_acces() est renommee en securiser_acces_low_sec() et definie dans inc/acces.
  • On garde un filtre_securiser_acces_dist() dans inc/filtres pour la compat des vieux squelettes
  • la fonction param_low_sec() est depreciee

Mises à jour des librairies

Nous avons mis à jour différentes librairies utilisées par SPIP.

Javascript

  • spip/spip -> Sortable 1.14.0
  • spip/spip -> jquery Form 4.3.0
  • spip/spip -> JS Cookie 3.0.1
  • spip/statistiques -> d3 7.3.0
  • spip/statistiques -> luxon 2.3.0
  • spip/plan -> jstree 3.3.12

PHP

  • spip/compresseur -> css-tidy 2.0.0
  • spip/medias -> getid3 1.9.21
  • spip/medias -> svg-sanitizer 0.14.1

Suppression de librairies

Nous avons supprimé jQuery.cookie qui était déprécié depuis SPIP 3.2 au profit de JS Cookie.

Pour rappel si vous n’aviez pas encore adaptés vos utilisations :

$.cookie(key) devient Cookies.get(key)
$.cookie(key, value) devient Cookies.set(key, value)
$.cookie(key, value, options) devient Cookies.set(key, value, options)

Interface

Les rubriques dans l’espace privé affichent maintenant aussi la liste des articles refusés.

Nettoyages

Nous avons supprimé

#FORMULAIRE_CONFIGURER_METAS

qui était déprécié depuis SPIP 3.0 au profit des formulaires

#FORMULAIRE_CONFIGURER_XX

. Voir la documentation Configurer une fonctionnalité de votre site, ou un plugin à ce sujet.

Quelques adaptations

Dans un squelette, pour avoir la liste des tables des objets éditoriaux, préférer #NULL (qui retourne null) plutôt que #REM (qui retourne une chaîne vide).

- [(#REM|lister_tables_objets_sql)]
+ [(#NULL|lister_tables_objets_sql)]

Plugins

Archiviste

Le plugin "Archiviste" a été mis à jour pour utiliser les extensions PHP Zip, zlib et Phar, qui sont désormais requises. Ceci nous a permis de supprimer les (très) vielles librairies Pcl* que nous traînions depuis quelques années.

À noter aussi que le plugin est accompagné de tests unitaires écrits avec PHPUnit.

Bigup

Le formulaire de configuration de Bigup est affiché sur la page des options avancées, avec une formulation un peu plus parlante pour le titre du formulaire d’explication.

Compresseur

Mise à jour de la librairie CSSTidy en version 2.0 et correction d’un bug dans la compression des fichiers Javascript sur certains environnements.

Forum

  • correction d’un bug qui affichait le lien vers la page de contrôle des forums pour les auteurs non autorisés.
  • normalisation de l’ajout d’un forum en base afin de bien faire passer les informations par les pipeline pre/post insertion
  • correction d’un bug d’affichage d’icône en langue RTL
  • correction d’un bug qui permettait d’accéder au forum privé des rédacteurs alors que celui-ci était désactivé dans la configuration
  • correction d’un bug dans la recherche de message depuis la page de contrôle des forums

Mediabox

  • amélioration de l’affichage des textes longs dans la légende des modales
  • utilisation d’un thème sombre par défaut dans l’espace privé
  • rétablissement du support des URLs avec fragments à l’aide d’un sélecteur dans l’attribut data-href-popin
  • ajout du support des attributs data-(max|min)-(width|height) et data-(width|height) sur les liens mediabox et rétablissement de la compatibilité avec les anciennes classes du type boxInline boxIframe boxWidth-xx boxHeight-xx
  • correction d’un bug avec certaines images distantes qui n’étaient pas affichées par la modale (notamment des SVG), car l’image n’a pas de dimension connue

Medias

  • permettre de personnaliser le comportement en autolien des images en fonction du media, de l’extension, de la taille ou de l’id_document
  • correction d’un bug dans le modèle d’embed des fichiers CSV
  • retrait de la librairie obsolète jQuery multifile
  • mise à jour des librairies SVG Sanitizer et getid3
  • retrait des préfixes navigateurs dans les CSS du plugin
  • amélioration UX des boutons dans le bloc des documents d’un objet
  • correction d’un bug dans le code généré pour les attributs d’une image avec légende
  • amélioration du modèle utilisé pour l’insertion des images SVG dans le texte d’un objet
  • correction d’un bug d’affichage de la médiathèque quand un document comporte une très longue URL dans les champs titre ou credit

Plan

Mise à jour de la librairie Jstree en version 3.3.12.

Révisions

Correction d’un bug qui empêchait le fonctionnement du génie d’optimisation des révisions.

Sites

  • correction d’un bug qui affichait la date en double dans les listes d’articles syndiqués
  • amélioration des formulaires d’édition de site en utilisant un placeholder dans les champs d’URL (au lieu de les pré-remplir avec http://)
  • correction d’un bug d’affichage de la liste des sites en colonne latérale de l’espace privé

Statistiques

  • mise à jour des librairies D3.js, d3-time-format et Luxon.js
  • correction d’un bug d’affichage des noms des mois dans les statistiques en langue arabe

SVP

  • correction d’un bug dans le déballage des archives de plugin avec les nouvelles fonctions du plugin Archiviste
  • amélioration de présentation des cases à cocher dans la liste des plugins
  • correction d’un bug de sélection des plugins quand l’utilisateur faisait "tout cocher" puis "cocher les mises à jour"
  • correction d’un bug dans l’affichage des messages de retour du formulaire de chargement de plugin
  • toujours afficher le lien vers la documentation du plugin qu’il soit actif ou non
  • correction d’un bug qui empêchait d’activer un plugin quand les téléchargements sont interdits

TextWheel

  • correction d’un bug qui échappait par erreur certains liens automatiques
  • correction d’un bug de direction d’affichage des puces quand la page contient des bloc de textes de directions différentes

URLs étendues

Pour faire suite aux changements d’API sur les URLs et objets, les fonctions d’URLs ont été reprises pour une écriture plus claire et mieux typée avec 2 points d’entrée pour la partie décodage/encodage.

Voir la demande d’ajout correspondante pour le détail complet.

Squelettes dist

  • correction d’un bug qui provoquait le débordement des paginations sur petit écran
  • application du filtre |header_silencieux sur les pages publiques afin de respecter la valeur de la globale spip_header_silencieux
  • retrait des classes spécifiques à Internet Explorer dans les CSS
  • correction d’un bug d’affichage des images et SVG sur petit écran

Auteur b_b, James, JLuc, Matthieu Marcillaud Publié le : Mis à jour : 05/02/24

Traductions : English, français