Voilà, voilà... Fin de l'aventure...

 

Fermeture de kolossaldrupal.org dans...


Bonjour tout le monde,

Drupal évolue, les versions changent et Kolossaldrupal.org était essentiellement consacré à la version 6 de Drupal.

Autant dire que les infos présentées ici commencent à dater...

Faute de temps, je ne peux plus garder le site Kolossaldrupal à jour...

Je vous aurais bien proposé de reprendre le flambeau mais... c'est tellement simple de nos jours de se faire son propre site à soi...Pourquoi s'embêter alors ? :-)

Ce site restera donc en l'état, tel qu'il était en 2011...

Ah la la ! Cela ne nous rajeunit pas !

Manuel Vila - Avril 2016

Manipuler le texte de façon sûre

Référence sur drupal.org : 12 Novembre 2008 – 04h55 - http://drupal.org/node/28984


Lorsque vous manipulez et affichez du texte en HTML, vous devez veiller à ce qu’un filtrage ou un échappement correct soit effectué. Autrement, il pourrait y avoir des bugs si des utilisateurs utilisent des crochets ou des esperluettes. Ou pire, vous pourriez ouvrir la voie à des attaques XSS.

Lorsqu’on manipule des données, la règle est d’enregistrer exactement ce que l’utilisateur a saisi. Lorsqu’il modifie un texte créé précédemment, le formulaire doit  contenir exactement le texte saisi auparavant. Ce qui veut dire que les conversions se font lors de l’affichage du contenu, pas lors de sa sauvegarde dans la base de données (lisez bien la documentation db_query() sur la façon d’utiliser l’API database de façon sûre).

Pour déterminer les contrôles à effectuer, il peut être pratique de distinguer les textes selon leur nature. S’agit-il de texte seul (plain text), HTML, Bbcode ou Textile ? De là, chaque fois que vous concaténez deux chaînes de caractères, vous devez vous assurer qu’elles soient du même format. Si elles ne le sont pas, une vérification adéquate, une conversion ou un filtrage doit être appliqué.

Dans Drupal, les données soumises par les utilisateurs peuvent être réparties en trois catégories :

1. Texte seul (plain text)

C’est du texte simple, sans aucun balisage. Ce que l’utilisateur saisit est affiché tel quel à l’écran, sans aucune interprétation d’aucune sorte. C’est habituellement le format utilisé pour les champs texte sur une seule ligne.

Lorsque vous affichez du plain-text, vous devez le passer par check_plain() avant de l’inclure dans du HTML. Cela convertit les guillemets, les esperluettes et les crochets en entités HTML, ce qui fait que la chaîne est affichée littéralement dans le navigateur.

Beaucoup d’API et de fonctions « thémables » prennent du HTML en argument et il y en a quelques-unes qui assainissent automatiquement le texte en le passant à check_plain() :

  • t() : les caractères de substitution (comme %name ou @name) sont passés en tant que texte pur (plain-text) et seront échappés lors de l’insertion dans une chaîne traduisible. Vous pouvez désactiver cet échappement en utilisant les caractères de substitution (placeholders) du formulaire !name! (plus d’infos)
  • l() : le texte du lien doit être passé en tant que plain-text (à moins qu’il ne soit surchargé avec le paramètre $html)
  • éléments de menu et fils d’Ariane (breadcrumb) : les titres des éléments de menu et le titre du fil d’Ariane sont automatiquement assainis.
  • theme(’placeholder’) : le caractère de substitution est du plain-text
  • Descriptions de blocs (mais pas les titres, voir plus bas)
  • Noms d’utilisateurs lorsqu’ils sont affichés avec theme_username()
  • Form API : les éléments #default_value et #options lorsque le type est une liste de sélection.

    Exemples :
  • <?php
    $form
    ['safe'] = array(
      
    '#type' => 'textfield',
      
    '#default_value' => $u_supplied,
    );


    $form['also_safe'] = array(
      
    '#type' => 'select',
      
    '#default_value' => 0,
      
    '#options' => node_get_types('names'),  // Could contain unsafe values but FAPI will pass through check_plain() before displaying to user.
    );
    ?>

Certains emplacements exigent que tout texte soit préalablement assaini :

  • Les titres de page obtenus via drupal_set_title().

    Le titre de la page est affiché en HTML, où il est logique d’utiliser des balises comme <em> pour des raisons de clarté. Cependant, lorsque le titre de la page est affiché dans la balise <HTML>, toutes les balises sont supprimées.

    Exemples :
    <?php
    drupal_set_title
    ($node->title); // XSS vulnerability, bad
    drupal_set_title(check_plain($node->title));  // Correct
    ?>
  • Les titres de blocs passés par hook_block(). Pour la même raison que pour le titre de page, l’utilisation d’HTML est ici courante.
  • Messages Watchdog.

    Exemples :
    <?php
    Drupal 5
    :
    watchdog('content't("Deleted !title", array('!title' => $node->title))); // XSS
    watchdog('content't("Deleted %title", array('%title' => $node->title))); // or @

    Drupal 6 (The message and variables are passed through t() by the watchdog function):
    watchdog('content'"Deleted !title", array('!title' => $node->title)); // XSS
    watchdog('content'"Deleted %title", array('%title' => $node->title)); // or @
    ?>
  • Éléments de formulaire #description et #title

    Exemples
    <?php
    $form
    ['bad'] = array(
    '#type' => 'textfield',
    '#default_value' => check_plain($u_supplied),  // bad: escaped twice
    '#description' => t("Old data: !data", array('!data' => $u_supplied)), // XSS
    );

    $form['good'] = array(
    '#type' => 'textfield',
    '#default_value' => $u_supplied,
    '#description' => t("Old data: @data", array('@data' => $u_supplied)),
    );
    ?>

  • Éléments de formulaire #options lorsque #type = chekcboxes ou #type = radios
    Exemples
    <?php
    $form
    ['bad'] = array(
      
    '#type' => 'checkboxes',
      
    '#options' => array($u_supplied0$u_supplied1),
    );

    $form['good'] = array(
      
    '#type' => 'checkboxes',
      
    '#options' => array(check_plain($u_supplied0), check_plain($u_supplied1)),
    );
    ?>
  • Éléments de formulaire #value de #type balisage et items doivent être sûrs. Notez que l’élément de formulaire #type est du balisage

    Exemples
    <?php
    $form
    ['unsafe'] = array('#value' => $user->name); //XSS
    $form['safe'] = array('#value' => check_plain($user->name));
    or
    $form['safe'] = array('#value' => theme('username'$user));
    ?>

2. Rich Text

Le Rich Text contient des balises HTML, Textile ou autres. Il est enregistré dans le format propre aux balises et converti en HTML lors de l’affichage grâce aux différents filtres activés. C’est le format généralement utilisé pour du texte multi-ligne.

Tout ce que vous avez à faire et de passer le rich text à check_markup() pour obtenir du HTML en retour, sûr pour l’affichage. Vous pouvez aussi permettre à l’utilisateur le choix du format de saisie, avec un widget de format via filter_form() et passer le format choisi à check_markup().

Vous devez vous assurer que l’auteur d’un message est autorisé à utiliser un format de saisie spécifique. Par sécurité, check_markup() effectué cette vérification pour l’utilisateur en cours par défaut. Toutefois, comme le contenu est filtré à l’affichage, ce n’est pas toujours la personnes à l’origine du contenu. Dans ce cas, vous devez désactiver cette vérification en passant $check = false à check_markup(), et en vous assurant que le format est contrôlé avec filter_access() lorsque le contenu est soumis.

3. Admin-only HTML

Depuis Drupal 4.7, il y a une troisième façon de traiter le texte. Il y a des parties de la section administration où l’on ne peut pas appeler le filtre système (pour le rich text) mais où des balises simples sont souhaitées, telles que des liens ou des italiques (donc le plain text n’est pas utilisable).

Ce peut-être le cas dans l’objectif du site, les règles d’utilisation et les descriptions de forums.

Pour ces cas-là, vous pouvez utiliser une zone de texte normale et passer le texte dans la fonction filter_xss_admin() pour l’affichage. Cela autorisera la plupart des balises HTML, tandis que cela bloquera les scripts ou les styles potentiellement nuisisbles.

Les URL dans Drupal doivent être manipulées de façon spéciale dans deux cas :

  1. Si des données dynamiques sont mises dans une URL, vous devez les passer dans urlencode(). Sinon, des caractères tels que # ou ? perturberont la sémantique de l’URL. urlencode() l’évitera en l’échappant avec la syntaxe %XX. Notez que les chemins Drupal (comme node/123) sont intégralement passés dans urlencode() depuis Drupal 4.7, il n’est donc pas nécessaire d’en urlencoder des parties. Ce confort ne s’applique pas aux autres parties de l’URL comme les arguments des requêtes GET ou les morceaux d’identificateurs.
  2. Quand vous utilisez une URL utilisateur en tant qu’hyperlien, vous devez utiliser check_url() plutôt que check_plain() uniquement. check_url() appelera check_plain() et effectuera également quelques contrôles XSS pour s’assurer de la sûreté de l’URL.

Notez que toutes les fonction Drupal qui retournent des URL (url(), request_uri(), etc) affichent du texte seul, qui n’a pas été échappé du tout. Pour les échapper avant un affichage HTML (ou XML), utilisez check_url(). Mais ne l’utilisez pas dans les cas où une véritable URL est attendue, comme dans le header Location : ….

En pratique

Toutes les règles ci-dessus peuvent se résumer à ceci : rien de ce qu’un utilisateur soumet comme contenu ne doit être restitué tel quel dans le code HTML. Si vous n’êtes pas sûr, vous pouvez le tester en soumettant un bout de texte comme <u>xss</u> dans les champs de votre module. Si le texte est restitué souligné ou mutile les balises existantes, vous savez que vous avez un problème.

Voici des exemples de code corrects et incorrects. $title, $body et $url sont censés être des saisies utilisateurs contenant respectivement un titre, un bout de texte balisé et une URL. Elles sont issues de la base de données  et contiennent ainsi exactement ce que l’utilisateur a soumis, sans modifications.

Incorrect :

<?php print '<tr><td>$title</td><td>'?> <?php print '<a href="/..." title="$title">view node</a>';?>

Correct (le titre est du texte seul et ne doit pas être placé tel quel dans le code HTML):

<?php print '<tr><td>'check_plain($title) .'</td></tr>'?> <?php print '<a href="/..." title="'check_plain($title) .'">view node</a>'?>

Incorrect :

<?php print l(check_plain($title), 'node/'$nid);  ?>

Correct (l() appelle déjà check_plain() par défaut ):

<?php print l($title'node/'$nid); ?>

Incorrect :

<?php print '<a href="/$url">';  ?> <?php print '<a href="/'check_plain($url) .'">';  ?>

Correct (les URL doivent être vérifiées avec check_url()):

<?php print '<a href="/'check_url($url) .'">';  ?>

Écriture de filtres

Lorsque vous écrivez des filtres qui convertissent un langage de balises en code HTML, vous devez veillez à ne pas créer vous-même des failles de sécurité. D’une manière générale on applique les mêmes règles : vérifiez les URL avec check_url() et veillez à ce qu’aucun code HTML littéral puisse être injecté en l’échappant correctement avec check_plain().