Aller au contenu

MySQL/Sécurité

Un livre de Wikilivres.

Sur Internet, des pirates tentent de récupérer des données en violant leurs accès. Ils insèrent souvent du code dans une page HTML ou PHP, par injection de code, généralement par[1] :

  • le biais d'un formulaire HTML qui permet d'entrer des données dans la base de données
  • ou alors en insérant des données par le biais de la barre d'adresse de la fenêtre,
  • par le dépôt d'un fichier sur le serveur (upload)
  • ou toute autre voie d'accès au serveur possible.

Les bases de données permettent d'assurer le stockage des informations en liaison par exemple avec des pages HTML couplées avec PHP. Donc tout le monde peut en avoir accès par l'intermédiaire d'un client relié au serveur. MySQL basé sur SQL est notamment très utilisé avec PHP. Il est donc pertinent d'examiner quelques notions de sécurité en matière de SQL.

En ce qui concerne SQL, il faut savoir premièrement qu'une astuce de programmation ou un détournement de requête possible à partir d'un serveur a de bonnes chances de réussir sur un autre et il en est de même avec les applications web mêmes, qui peuvent être utilisées afin d'atteindre le serveur et d'en saccager les données.


Paramètres de connexion

[modifier | modifier le wikicode]

Parfois, les paramètres de connexion (dont login et mot de passe) sont stockés dans un fichier texte non crypté, comme un .ini. Cela n'est donc pas recommandé : si un utilisateur devine son nom il peut le lire sans peine. S'il est situé en dehors du répertoire WWW c'est mieux, mais la meilleure façon est de les enregistrer dans un programme (ex : .php).

Il est toujours possible pour un utilisateur de trouver les accès FTP, donc il vaut mieux en utiliser d'autres pour MySQL.

Inutile de se souvenir des mots de passe MySQL car le programme est censé le retrouver automatiquement. Il convient donc d'en choisir un robuste : très long, avec au moins une majuscule, une minuscule, un symbole (ex : '_') et un chiffre. Le tout sans contenir de mots du dictionnaire car ils sont utilisés pour accélérer les crackages lors d'attaque par force brute.

Logo

Ne jamais les stocker autre part, y compris dans des e-mails.

Injections SQL

[modifier | modifier le wikicode]

Normalement les valeurs stockées dans $_POST peuvent être insérées directement dans des requêtes SQL. Toutefois les injections SQL exploitent une faille de sécurité engendrée par le caractère d’échappement.

Soit le code PHP suivant qui récupère les données d'un formulaire :

$sql = "DELETE FROM articles WHERE id = " . $_POST['article_id'];

Si on remplit le champ du formulaire avec le nombre "42", le code s'exécute normalement :

DELETE FROM `articles` WHERE `id`= 42       -- supprime une ligne


Mais si on ajoute une expression booléenne toujours vraie ensuite, "42 OR 1=1", cela change complètement le résultat :

DELETE FROM `articles` WHERE `id`= 42 OR 1=1  -- supprime toutes les lignes (car 1=1 toujours vrai)


L'utilisateur peut aussi insérer d'autres commandes à la suite, séparées par des points-virgules. Il s'agit du chainage (chaining SQL queries). Ex :

DELETE FROM `articles` WHERE `id`= 42; DROP TABLE `users`;


Soit le code PHP suivant :

$sql = "SELECT * FROM `users` WHERE login = '" . $_POST['user_login'] . "'";
 SELECT * FROM `table1` WHERE title='bla bla'                           -- sélectionne

Dans le cas des chaînes de caractères, il faut exploiter les apostrophes autour :

 SELECT * FROM `table1` WHERE title='bla bla'; TRUNCATE TABLE `table1`  -- supprime tout

Pour obtenir cette requête, il faut remplir le champ de formulaire pour fermer l'apostrophe ouvrant puis commenter le fermant :

bla bla'; TRUNCATE TABLE `table1`--

Par ailleurs, si un utilisateur découvre comment manipuler une base de cette façon, il peut très bien se créer un compte administrateur ensuite, et effectuer des modifications discrètes dans le but de récupérer de l'argent (fausses factures, phishing...).

Blind SQL injection
[modifier | modifier le wikicode]

Si le code PHP empêche les injections SQL ci-dessus, il est potentiellement tout de même possible d'obtenir des informations en injectant des expressions booléennes vraies et fausses pour en comparer les retours de formulaire.

Par exemple, si les retours différent pour :

x' and 1=1--
x' and 1=0--

alors le champ rempli peut être exploité :

x' and substring(password, 1, 1)='a
x' and substring(password, 1, 1)='b
x' and substring(password, 1, 1)='c

...

Il faut vérifier que la variable stocke bien le type de données prévu :

  • Pour les chaînes de caractères : comme elles sont entourées d'apostrophes, tous ceux qu'elles contiennent doivent être convertis (ex : en '' ou \'). Par exemple, PHP propose mysql_real_escape_string pour gérer ces substitutions.
  • Pour les dates : les entourer d'apostrophes comme les chaînes.
  • Pour les noms SQL (tables, champs...) : les entourer d'apostrophes comme les chaînes.
  • Pour les nombres : s'ils contiennent autre chose que des chiffres, ils ne conviennent pas.
  • NULL / UNKNOWN / TRUE / FALSE : ne doivent jamais être rentrées par l'utilisateur.
  • Tronquer la valeur au nombre de caractères supporté par le champ correspondant.

Par ailleurs, aucun commentaire SQL ne devrait pourvoir être inséré par l'utilisateur.

Généralement ils sont chiffrés ou hashés puis dans la base par les applications. Par contre si c'est fait en SQL, c'est qu'ils ont été écrits au moins une fois en clair et donc été visibles :

  • dans les logs du serveur,
  • dans les logs MySQL,
  • dans SHOW PROCESSLIST.

Il est donc fortement déconseillé d'envoyer ce genre de commandes :

SELECT 1 FROM `clients` WHERE `password`=MD5('abraxas')

Préférer depuis un .php par exemple :

$sql = "SELECT 1 FROM `clients` WHERE `password`='".md5('abraxas')."'";

Il est également déconseillé de ne rechercher que le mot de passe, il faut que le nom corresponde aussi, et utiliser une fonction de hachage cryptographique plus récente comme SHA256 :

$sql = "SELECT 1 FROM `clients` WHERE `name`='".username."' AND `password`='".hash('sha256','abraxas')."'";
  • Ne jamais utiliser de fonctions de cryptage non sécurisées, comme PASSWORD().
  • Ni de cryptage réversible (se contenter de comparer si un chaîne cryptée est égale à une autre chaîne cryptée comme authentification). Ou bien les mots de passe stockées dans une base doivent être impossibles à sélectionner (avec SELECT).
  • Seulement du hachage cryptographique, comme SHA256, pas d'algorithmes plus vieux comme MD5.

Si le contenu de la base est publique, il n'y a pas de raison de crypter les communications.

Toutefois il peut y avoir un accès restreint pour les droits d'écriture, ce qui nécessite des mots de passe.

Le cryptage SSL s'avère une bonne solution pour cela. En effet, en plus de crypter les messages, il certifie que l'utilisateur est bien celui qui était prévu (même si ce dernier a donné son mot de passe par phishing).

Risques dans les manipulations de données

[modifier | modifier le wikicode]

On en retrouve plusieurs types :

  • Contournement d'une clause where, c'est-à-dire neutraliser la clause WHERE en injectant une condition constante (toujours vraie ou toujours fausse, comme par exemple : SELECT * FROM table WHERE 1;
  • Modification sans limite, comme par exemple la destruction totale de données avec la commande DELETE : DELETE FROM table WHERE 1;
  • Insertions indésirables, où le pirate peut injecter une ligne complète qui sera traitée à son insu par la base de données. L'application insère ainsi des valeurs dont elle n'est plus maîtresse, par exemple le pirate peut injecter : md5('secret') sans un formulaire, ce qui revient à crypter le mot 'secret', que la base de données ne pourra plus retrouver ou traiter.
  • Exécutions multiples : par exemple une injection pour placer une requête dans une autre requête initiale, afin d'exécuter toutes les injections que veut le pirate.
  • Surcharger le serveur : c'est-à-dire modifier une requête afin qu'elle engendre une charge de travail importante et inutile pour le serveur.
  • Exportations cachées : en exportant des données, en les extrayant de leur dépôt habituel et les placer par la suite dans un réceptacle public plus facile à lire. Cela ce fait par exemple en contournant la clause WHERE, ou alors en insérant des fichier externes directement sur le serveur SQL.

Chiffrement d'un mot de passe

[modifier | modifier le wikicode]

Le chiffrement d'un mot de passe ou d'une donnée consiste en sa transformation en une autre donnée par le biais d'une méthode extrêmement difficile à inverser voire impossible avec les fonctions de hachage. MySQL offre des méthodes de chiffrement des données et par la même du mot de passe. Nous avons par exemple :

INSERT INTO utilisateurs(identifiant, motdepasse) VALUES ('$id', MD5('$mdp'));

-- Vérification :
SELECT id FROM utilisateurs WHERE identifiant = '$idTest' AND motdepasse = MD5('$mdpTest');

-- Pour du contenu sécurisé à afficher : 
--  - Stockage :
INSERT INTO dossiersSecrets(titre, contenu) VALUES ('$titre', AES_ENCRYPT('$contenu','motdepasse'));

--  - Récupération :
SELECT titre, AES_DECRYPT(contenu,'motdepasse') AS contenu FROM dossiersSecrets;

Sécurité PHP

[modifier | modifier le wikicode]

Voici quelques types d'attaques que l'on retrouve fréquemment :

  • l'injection de code distant.

Par exemple, c'est introduire une instruction PHP dans une page, qui ira chercher du code PHP sur un site distant pour le ramener sur le serveur local du pirate et l'exécuter comme code local, afin de prendre contrôle du serveur et des données du serveur distant piraté.

  • l'exécution du code à la volée.

Cela ce fait par exemple à l'aide de : eval(), fonction qui prend en argument une chaîne de caractère pour l'exécuter comme du code PHP ; et par là exécuter des opérations destructrices de données par cette voie.

  • le téléchargement de fichiers ; ce qui permet d'importer du code PHP sur un site.

Il suffit en effet que ce site ait des autorisations de lecture et qu'il soit accessible depuis le Web, sans besoin de droit d'exécution ou d'écriture pour exécuter tous les codes PHP que veut le pirate. En stockant le fichier, par la fonction "upload" (ex. image), le code est exécuté sur le serveur pirater et vient en saboter ou déranger le fonctionnement et même la lecture des autres documents.

  • La directive register_globals;

Si elle est activée permet d'injecter dans les scripts toutes sortes de variables ; ce qui rend la programmation de script peu sûr. Heureusement que dans les versions supérieures à PHP 4.2 l'option register_globals est désactivée par défaut.

  • filtrage des données ;

C'est-à-dire la gestion des erreurs, ou les mécanismes par lesquels l'application web déterminera la validité des données qui entrent et sortent de l'application. Il s'agit donc de s'assurer que : premièrement on ne puisse pas contourné le filtrage des données, que deuxièmement les données invalides ne puissent pas être confondues avec des données valides et enfin identifier l'origine des données.

Vous trouverez les solutions possibles à ces problèmes dans ce guide de sécurité PHP en ligne : http://phpsec.org/.

La solution des systèmes de gestion de contenu ou de création de site web

[modifier | modifier le wikicode]

Une autre solution est d'utiliser des systèmes de création et de gestion de contenu Web, pour créer un site web personnel et l'agrémenter d'une petite bases de données. L'avantage ici est que si vous utilisez par exemple une application comme http://sites.google.com/.


L'avantage est que la sécurité est ici gérée par de grandes sociétés comme ici 'Google' qui engage des experts en la matière. Il devient dès lors très difficile pour des pirates de saccager les données de l'application web que vous créez, ainsi que la base de données.

Sites.google, par exemple, est une application en ligne qui permet le plus simplement possible de créer un petit site Internet perso agrémenter d'une base données. Pas besoin ici de sécuriser les données c'est la société Google, qui s'en charge ! Par exemple, Google.site permet de rassembler rapidement en un seul endroit une série d'informations, tout en sécurité :

  • vidéos
  • agendas,
  • présentations,
  • informations stockées dans une base de données,
  • pièces jointes,
  • texte.

Elle permet également en matière de sécurité d'autoriser leur accès ou leur modification à un groupe restreint, ou carrément au monde entier. Encore une fois, il y a ici la garantie de Google en matière de sécurité des informations.

Voici cependant quelques mesures de sécurité qui sont offertes à l'utilisateur :

  • supprimer quelqu'un d'un site ;
  • contrôler l'accès au site ;
  • supprimer des commentaires et des pièces jointes ;
  • système de login avec mot de passe.

En matière de mot de passe il est à noter qu'il s'agit d'avoir sous la main trois types de mot de passe selon leur utilité : un pour des applications banales sans importance que vous utilisez, l'autre pour des applications personnelles (ex. e-mail), et l'autre enfin si possible plus complexes pour des informations hautement importantes et confidentielles que vous désirez gérer depuis Internet (ex. accès pour un site bancaire).

Ouverture à un PC distant

[modifier | modifier le wikicode]

Par défaut le serveur MySQL n'écoute pas sur le réseau. Pour changer cela, il faut préciser son IP publique dans la configuration[2] :

vim /etc/mysql/mysql.conf.d/mysqld.cnf

ajouter ou modifier la ligne :

bind-address = MonIPPublique

Si le problème persiste, vérifier que le port 3306 est bien ouvert sur le pare-feu.

Les droits d'une vue peuvent dépendre se son créateur ou de ses utilisateurs. Pour les définir, utiliser SQL SECURITY INVOKER et SQL SECURITY DEFINER[3].

Pour localiser les vues qui n'ont pas ces droits (et peuvent donc renvoyer des erreurs de permissions) :

select table_schema, table_name, SECURITY_TYPE
from information_schema.views
where SECURITY_TYPE <> 'INVOKER';