Protection de vos scripts PHP : validation des données, XSS, CSRF,…

Aujourd’hui je commence une série d’articles sur quelques points de sécurité et de bon sens avec PHP.
Ces articles n’ont et n’auront rien de bien original par rapport à ce que l’on peut glaner de ci de là sur Internet, mais ils pourront me ou vous servir d’aide mémoire ou de piqûre de rappel !

Validation et filtrage des entrées

Un maître mot : « Ne jamais faire confiance aux données étrangères » !
Chaque donnée reçue doit être validée pour s’assurer qu’elle corresponde à ce que l’on attend réellement.

En PHP il existe de très nombreux outils de validation des données :

  1. Les opérateur de comparaison, de taille (chaînes, tableaux),…
  2. L’utilisation de liste de valeurs autorisées (« white-list ») : isset($hash[$var]) ... in_array($var, $allowed)
  3. À l’aide d’expression régulières : preg_match(‘/…/’ , $var);
  4. Extension ctype : ictype_digit($var); qui va vérifier que $var ne contient que des chiffres
  5. Les fonctions is_* : is_scalar($var);
  6. Depuis l’extension PHP 5.2, l’utilisation de l’extension Filter est recommandée.
    Exemple d’utilisation  :

    $clean['param1'] = filter_input(INPUT_POST, 'param1', FILTER_VALIDATE_BOOLEAN);
    $clean['email'] = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    
  7. Pour Zend l’utilisation des classes Zend_Validate_* ainsi que la création de vos propres validateurs.

Ces validations doivent s’appliquer sur les données reçues par : $_GET, $_POST, $_REQUEST, $_COOKIES, $_FILES et certaines en provenance de $_SERVER ($_SERVER['PHP_SELF'] par exemple.

Échappement des sorties

Une fois vos données validées, il reste à échapper les sorties afin d’éviter les attaques XSS (Cross-site scripting) : détournement de formulaires, modification de l’affichage, vols de cookies, de sessions, etc.

De même que pour la validation des données PHP contient tout le nécessaire pour effectuer ces opérations :

  • Protéger les caractères sensibles : htmlspecialchars($var);
  • Protéger tous les caractères : htmlentities($var,...) et mb_htmlentities($var,...). Le deuxième paramètre, ENT_COMPAT|ENT_QUOTES|ENT_NOQUOTES est parfois important.
  • Enlever les balises HTML : striptags($text);
  • Encodage spécique pour URL : urlencode($var);
  • L’extension Filter peut non seulement valider comme vu au-dessus, mais aussi filtrer en sortie avec FILTER_SANITIZE_*
  • Pour Zend l’utilisation des classes Zend_Filter_* est tout aussi aisée, avec la possibilité de créer ses propres filtres suivant ses besoins

Attention au jeu de caractères, en particulier pour htmlentities() : problème d’affichage, voire de sécurité.
Pour les charsets multi-octets (comme UTF-8), mb_htmlentities() est plus fiable.

Protection des formulaires

S’assurer que les champs cachés ne sont pas modifiés

Pour cela il suffit d’envoyer en parallèle un hash contrôlant la valeur du champ caché.


Et à la réception des données, vérifier que le hash correspont bien à la valeur attendue :

$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
$idhash = filter_input(INPUT_POST, 'idhash', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if ($idhash !== md5($id . SALT) {
die('Le formulaire est louche ! ' );
}

Se prémunir des CSRF (Cross-Site Request Forgery)

Les attaques CSRF consistent à faire exécuter des commandes involontaires aux utilisateurs accrédités d’un site.

Quelques astuces pour s’en prémunir ou au moins en limiter grandement la portée, certaines évidentes et facile à mettre en place, d’autres plus subtiles…

Demander des confirmations à l’utilisateur pour les actions critiques

Par exemple systématiquement demander une confirmation du style « Êtes vous certain de vouloir … » sur des actions sensibles comme la suppression, ou redemander de saisir le mot de passe lors de la modification de la configuration de votre site internet.

Utiliser des jetons de validité dans les formulaires

Le principe est simple : lorsqu’un utilisateur affiche un formulaire, on lui génère une clé. Cette clé sera valide pour un certain temps (quelques minutes, le temps de remplir le formulaire), et est liée uniquement au couple utilisateur/formulaire. Cette clé devra automatiquement être transmise avec le formulaire pour que celui ci puisse être validé !

Dans le framework Zend il existe un élément de formulaire dédié à cet usage : Zend_Form_Element_Hash, son utilisation est aisée :

class Mon_Formulaire_anti_csrf extends Zend_Form {
  public function init() {
    $this->setMethod('post');
    $this->addElement( 'submit'
                     , 'submit'
                     , array( 'ignore'   => true
                     , 'label'    => 'Soumettre'
                     ,)
                     );
    $form->addElement( 'hash'
                       , 'no_csrf'
                       , array( 'salt' => 'unique')
                       ); 
}

Le hash généré est stocké en session et sera ajouté à la chaîne de validation du formulaire : s’il est différent de celui stocké, il y aura une erreur d’émise.

Éviter d’utiliser des requêtes HTTP GET pour effectuer des actions

Passer systématiquement par POST pour toutes les actions autres que celles de consultation de ressource : insertion, mise à jour ou suppression de données.
Cette mesure va vous prémunir des attaques simples basées sur les images, mais pas de requêtes HTTP POST forgée en JavaScript par exemple.

Quelques liens pour aller plus loin :

  1. http://fr.wikipedia.org/wiki/Cross-site_request_forgery
  2. Zend_Form_Element_Hash
  3. http://bigornot-fr.blogspot.com/2008/07/csrf-sea-surf-and-zend.html
  4. http://truffo.fr/2010/03/les-filtres-php/
  5. http://zendframework.com/manual/fr/zend.filter.html
  6. http://zendframework.com/manual/fr/zend.validate.html
  7. Extension Filter

Laisser un commentaire

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

*

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