Sécuriser les plugins Spip (versions 2 et 3)

Spip est un CMS utilisé de manière massive par les institutions françaises. Ces dernières étant susceptibles de subir de nombreuses tentatives de piratage, il est vital de sécuriser avec finesse des plugins destinés aux ministères. Nous allons voir comment contrer les différents types de menace en se basant sur l’API de Spip.

Ériger une protection contre les injections SQL

Il s’agit d’un grand classique : le hacker utilise le fait qu’une entrée utilisateur engendre bien souvent une requête en base de données pour insérer une chaîne soigneusement choisie (avec notamment une apostrophe bien placée) qui va affecter ladite requête. Cela peut permettre de se connecter en tant qu’administrateur ou même de vider la base de données !

L’API SQL de Spip permet de sécuriser des chaînes grâce à la fonction sql_quote() (http://programmer2.spip.net/sql_quote), qui va échapper le contenu de la manière attendue par le serveur SQL utilisé. Quand il s’agit de sécuriser un entier tel qu’un identifiant numérique, on peut également utiliser cette fonction ou simplement la fonction PHP intval().

Les fonctions sql_insert() (http://programmer2.spip.net/sql_insert) et sql_update() nécessitent l’utilisation d’une telle protection pour chaque donnée à insérer, ce qui peut être assez pénible quand il s’agit d’insérer des données complexes. Par ailleurs, ces fonctions connaissent des problèmes de portage SQL. On leur préférera donc leurs variantes, qu’on appelle en utilisant le même nom mais auquel on ajoute en suffixe la lettre « q » : il s’agit donc de sql_insertq() (http://programmer2.spip.net/sql_insertq,590) et sql_updateq(). Les valeurs envoyées à la BDD pour insertion ou mise à jour seront automatiquement échappées !

Il y a toutefois une subtilité non explicitée par la documentation de ces fonctions : les arguments de la clause 'where' ne sont pas échappés ! Il faut donc veiller à sécuriser la clause 'where' même en utilisant les fonctions dites sécurisées. Cela vaut pour la plupart des fonctions de l’API SQL, notamment sql_delete().

Combler les failles XSS

Encore une faille très connue : afficher un texte entré par l’utilisateur sans vérification de son contenu peut lui permettre d’insérer sur le site des balises HTML et du Javascript (par exemple quand l’utilisateur poste un commentaire sur un article, ou écrit un message sur un forum). Or le Javascript peut occasionner des désagréments pour les utilisateurs, mais aussi être utilisé pour rediriger vers un autre site pour de l’hameçonnage, voire permettre au pirate de récupérer les identifiants / sessions des utilisateurs via leurs cookies !

Le moteur de template de Spip propose le filtre |entites_html (équivalent à la fonction PHP html_entities()) afin d’échapper les caractères spéciaux comme les chevrons, et ainsi éviter de pouvoir insérer un code Javascript dans les pages affichant des données issues d’entrées utilisateurs.

Calfeutrer les accès aux fichiers

Tout d’abord, notons qu’on peut protéger les fichiers qui ne correspondent pas à une page (mais qui sont appelés par un autre script) avec l’instruction suivante, au début du fichier :

if (!defined("_ECRIRE_INC_VERSION")) return;

Comme le dit la documentation Spip (http://www.spip.net/fr_article3633.html) :

Dans Spip, la plupart des scripts ne sont pas exécutables directement mais uniquement incluables. Ils sont d’ailleurs en général "protégés" par un test initial :
if (!defined("_ECRIRE_INC_VERSION")) return;

Ceci vérifie que la constante _ECRIRE_INC_VERSION est définie, ce qui ne sera pas le cas si le script est chargé directement.
Cette constante est définie par ecrire/inc_version.php qui constitue la clé de voute de l’architecture SPIP. Ce script est en effet inclus pratiquement en tout début par les index public ou privé.

Quant aux pages générées par le plugin, on fait appel tout d’abord au pipeline autoriser :

Dans le fichier plugin.xml

<pipeline>
   <nom>autoriser</nom>
   <inclure>monplugin_autorisations.php</inclure>
</pipeline>

Dans le fichier monplugin_autorisations.php :

function monplugin_autoriser(){}
// Déclarations d'autorisations
function autoriser_accueil_voir_dist($faire, $type, $id, $qui, $opt) {
	//On autorise seulement les administrateurs à voir la page d’accueil du plugin
	return in_array($qui['statut'], array('0minirezo'));
}

Une fois l’autorisation déclarée, on peut l’utiliser ainsi dans le template :

[(#AUTORISER{voir, accueil})
	Le contenu nécessitant l’autorisation…
]

Si on veut utiliser des conditions plus complexes, il peut être intéressant d’utiliser la boucle CONDITION du plugin spip_bonux :

<BOUCLE_si_autorise(CONDITION) { si ( #VAL{id}|intval|et{#AUTORISER{voir,accueil} } ) } >

Si le template contient des instructions PHP complexes, l’usage des autorisations avec Spip risque de ne pas fonctionner. On peut alors créer un substitut avec la ligne PHP suivante (en début de page) :

// Ici, seul un administrateur pourra afficher la page
if ($GLOBALS['auteur_session']['statut'] !== '0minirezo') 
    die("Autorisation d'acces refusee !");

Conclusion

Nous avons vu que des solutions simples existent pour sécuriser efficacement un plugin Spip. On recommandera, à chaque fois qu’un plugin est achevé, de faire un audit de sécurité sur ledit plugin en vérifiant les trois points Injections SQL – Failles XSS – Autorisations.

Laisser un commentaire

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

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.