Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

La base de données est un élément critique dans l'architecture d'une application d'entreprise. Celle-ci se doit de pouvoir traiter efficacement de grandes quantités de données. Et elle peut être interrogée simultanément par de nombreux clients – éventuellement au travers d'un réseau public comme internet. Dans ces conditions on comprend qu'il est plus que nécessaire de pouvoir protéger les informations qui transitent entre la base de données et ses clients. Nous allons voir dans cet article comment utiliser SSL pour sécuriser la communication avec un serveur de bases de données MySQL.

Préambule

Créer la base

Je suppose ici que vous avez déjà installé MySQL et que vous êtes un minimum familier de son utilisation. Je vais donc rapidement passer sur la création de la base qui nous servira de support. Notez tout de même que je crée au passage un utilisateur autorisé à faire des requêtes SELECT sur les tables:

CREATE DATABASE MyDVDRental;
 
-- La table des DVD loués
CREATE TABLE MyDVDRental.CheckedOut(
       title CHAR(30) NOT NULL,
       client CHAR(30) NOT NULL);
INSERT INTO MyDVDRental.CheckedOut(title, client)
       VALUES ('Gone with the Wind', 'John DOE')
              ,('My Fair Lady', 'Rock SMITH')
              ,('Rambo II', 'John DOE')
              -- etc. etc.
              ;
 
-- Donne à l'utilisateur "audit" le droit d'interroger
-- la table des DVD loués. 
GRANT SELECT ON MyDVDRental.CheckedOut
       TO audit@'%' IDENTIFIED BY 'password1234';

Interroger la base avec le client MySQL

Comme vous le constatez, les informations stockées dans cette table sont critiques. Et vous comprenez bien que les clients n'ont pas envie que n'importe qui sache quels films ils ont loués... Faisons justement une requête qui permet d'extraire ces informations. Comme vous le remarquerez, ici cette requête sera effectuée sous l'identité de audit, l'utilisateur légitime de la base, dûment authentifié par son mot de passe:

sh$ mysql  -h chicoree -u audit -p MyDVDRental
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 847
Server version: 5.0.51a-24+lenny2 (Debian)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.00 sec)

Rien d'extraordinaire, non? Mais la question qui nous intéresse ici est de savoir ce qui est passé "sur la ligne". Autrement dit sur le réseau. Avec un logiciel de capture comme wireshark ou tcpdump voici ce que l'on peut capturer lors de cette session:

Image:MySQL TCP Stream.png

Au premier coup d'oeil, on voit que certaines données passent en clair. Sans nécessairement vouloir faire l'analyse détaillée de l'échange entre le client et le serveur, voici ce que l'on peut remarquer:

  1. L'identifiant audit apparaît en clair dans la trame.
  2. Le mot de passe lui n'est pas en clair. La lecture de la documentation de MySQL nous apprend que ce qui est échangé entre le client et le serveur est un hash du mot de passe. Ce code de hachage change à chaque session car un grain de sel (salt) est envoyé par le serveur à l'initiation de la communication. Cette valeur change à chaque connexion et participe au calcul du hash.
  3. Les requêtes du client passent en clair.
  4. Les données renvoyées passent en clair.

Déjà pas mal, non?

Attention:

Vous pourriez être tentés de vous dire que puisque le mot de passe qui sert à identifier l'utilisateur lors de la connexion est crypté, il n'y a aucun danger qu'un assaillant puisse capturer des mots de passe utilisateur avec cette configuration.

C'est faux. Considérez par exemple les requêtes suivantes qui impliquent le mot de passe d'un utilisateur:

SET PASSWORD = PASSWORD('somepass');
CREATE USER someone@somewhere IDENTIFIED BY 'somepass';
GRANT SELECT ON somebase.sometable ON someone@somewhere IDENTIFIED BY 'somepass';

Il s'agit là de requêtes transmises au serveur. Au même titre que le SELECT de la capture ci-dessus. Donc si un assaillant surveille la ligne, il verra passer ces mots de passe en clair!

Connexion sans SSL.png

Connexion sans SSL — Dans le cas d'une connexion sans SSL, rien ne garantit l'identité du client ou du serveur. Par ailleurs, un assaillant peut capturer – voire modifier – les données qui transitent.


Utiliser SSL pour sécuriser la connexion

Une solution pour supprimer les risques de captures d'information par écoute directe, est d'utiliser la possibilité pour MySQL d'établir une connexion utilisant SSL (Secure Sockets Layer) entre le client et le serveur. C'est ce que nous allons faire maintenant.

SSL en quelques mots

SSL permet de crypter la communication entre deux machines. Et donc de communiquer de manière sécurisée au travers d'un réseau ouvert comme internet. Outre le cryptage à proprement parler, SSL dispose également de mécanismes qui permettent de garantir l'intégrité des données transmises, de détecter la perte de données ou encore le fait que des trames soient répétées ou permutées.

Par ailleurs, SSL permet aussi d'authentifier les machines, les logiciels et/ou les utilisateurs grâce à un certificat X.509.

Dans le principe, la procédure pour obtenir un certificat X.509 est assez simple: il vous suffit de transmettre une demande à une autorité de certification (CA – Certificate Authority). Celle-ci, après vérification des informations contenues dans la demande, vous renvoie le certificat signé. Autrement dit, l'autorité de certification agit comme un tiers de confiance pour garantir l'exactitude des informations contenues dans le certificat.

SSL tiers de confiance.png

SSL tiers de confiance — Un tiers de confiance diffuse largement ses certificats racine (1). Un utilisateur peut demander à ce tiers de lui signer son propre certificat (2). Quand ce certificat est diffusé (3), son intégrité peut être vérifiée à l'aide du certificat racine du tiers de confiance (4).


Certificat racine

Pour que ce mécanisme fonctionne, il va de soi que celui qui veut prouver son identité, et la personne à qui il veut le prouver doivent être d'accord pour accepter le même tiers de confiance.

L'autorité de certification peut être une entité publique (comme un état) ou une société privée (Verisign, Entrust, QuoVadis, etc).

L'identité de ces tiers est garantie elle aussi par l'utilisation de certificats appelés certificats racine qui ont la propriété d'être bien connus. Autrement dit, largement diffusés: vous en avez vraisemblablement un certain nombre sur votre ordinateur, qui ont été installés avec votre client web, votre client de messagerie, SSH, etc.

Certificat racine autosigné

Il est aussi possible de générer soi-même des certificats racine. Dans ce cas, on parle de certificats autosignés. Ceux-ci peuvent être utilisés exactement comme les certificats racine émis par une autorité. La différence? Et bien, comme il n'y a plus de tiers de confiance connu impliqué pour garantir votre identité, si vous émettez vos propres certificats racine, vos correspondants devront vous faire confiance. Par ailleurs, il faudra aussi diffuser de manière fiable votre certificat racine auprès de vos clients.

Enfin, puisque vous disposez également de la clé privée associée à ce certificat, vous êtes en mesure de signer vous-même d'autres certificats. Exactement comme une autorité de certification.
Avec un logiciel comme OpenSSL, il est possible de générer une clé privée et son propre certificat racine ainsi:

sh$ mkdir certificats
sh$ cd certificats
# Génère une clé 2048 bits pour signer le certificat
sh$  openssl genrsa 2048 > ca-key.pem
Generating RSA private key, 2048 bit long modulus
.....+++
.......................+++
e is 65537 (0x10001)
# Crée un certificat autosigné
sh$ openssl req -new -x509 -nodes \
               -days 500 -key ca-key.pem > ca-cert.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:France
[...]

Après ces manipulations vous aurez dans le fichier ca-cert.pem un certificat racine autosigné utilisable exactement comme un certificat émis par une autorité ... à ceci près que personne (sauf vous) ne garantit l'exactitude des informations contenues dans ce certificat!

Certificat serveur

Pour que le serveur puisse prouver son identité, il est nécessaire que celui-ci fournisse un certificat signé par une autorité de certification. Tout d'abord, il faut commencer par générer une demande de certificat:

sh$ openssl req -newkey rsa:2048 -days 500 \
           -nodes -keyout server-key.pem > server-req.pem
Generating a 2048 bit RSA private key
...........................................................................+++
.........................................................................................................+++
writing new private key to 'server-key.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:France
[...]
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:aCp@H0m3
An optional company name []:

Après cette étape, vous disposez d'une demande de certificat destinée à être envoyée à l'autorité pour vérification et signature. Avec toutes les réserves évoquées plus haut, si vous ne voulez pas faire intervenir (hum ... payer) un tiers de confiance pour signer votre certificat, il est possible de le signer avec un certificat racine autosigné. Avec OpenSSL, voici comment procéder (je suppose que vous avez dans le répertoire courant le certificat ca-cert.pem et sa clé ca-key.pem):

sh$ openssl x509 -req -in server-req.pem -days 500 \
               -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 \
               > server-cert.pem
Signature ok
subject=/C=FR/ST=France/O=chicoree/CN=Sylvain
Getting CA Private Key

Certificat client

SSL permet éventuellement d'authentifier les deux parties d'une communication (le serveur et le client, par exemple). Cela nous servira dans un instant. On procède exactement de la même manière pour obtenir le certificat client que le certificat serveur:

sh$ openssl req -newkey rsa:2048 -days 500 \
           -nodes -keyout client-key.pem > client-req.pem
[...]
sh$ openssl x509 -req -in client-req.pem -days 500 \
               -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 \
               > client-cert.pem
[...]

Configurer le serveur pour utiliser un certificat

Vous pouvez configurer le serveur MySQL pour qu'il utilise un certificat pour prouver son identité et crypter la communication en modifiant le fichier de configuration de my.conf. Vous devrez rajouter les options ssl-ca, ssl-cert et ssl-key. Recherchez la section [mysqld] et modifiez ces options en conséquence:

[mysqld]
[...]
ssl-ca=/etc/mysql/ca-cert.pem
ssl-cert=/etc/mysql/server-cert.pem
ssl-key=/etc/mysql/server-key.pem

Il vous faudra aussi placer la clé du serveur ainsi que les certificats du serveur et de l'autorité à l'emplacement indiqué:

sh# cp server-key.pem server-cert.pem ca-cert.pem /etc/mysql

Et bien sûr, relancer le serveur MySQL:

sh# /etc/init.d/mysql restart
Stopping MySQL database server: mysqld.
Starting MySQL database server: mysqld.
Checking for corrupt, not cleanly closed and upgrade needing tables..

Se connecter avec le client avec SSL

Pour le client, vous pouvez également utiliser my.cnf pour le configurer. Ou à la place passer les arguments nécessaires sur la ligne de commande au moment de son invocation:

sh$ mysql --ssl-ca=ca-cert.pem \
             -u audit -h chicoree -p  MyDVDRental
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 55
Server version: 5.0.51a-24+lenny2 (Debian)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.05 sec)

A ce stade, si vous faites une capture des échanges entre le client et le serveur, vous constaterez que les seules informations qui transitent en clair sont les informations (publiques) relatives à l'autorité de certification. Tout le reste de la transaction (identifiant de connexion MySQL, requêtes et réponses) passent désormais en crypté.

MySQL capture SSL.png
Capture d'une session MySQL utilisant SSL. Remarquez en haut les informations sur l'autorité de certification. Ce sont les seules à passer en clair. Remarquez également que l'utilisation de SSL augmente le volume de données transitant entre le client et le serveur.



Forcer l'utilisation de SSL

SSL.png

Tunnel SSL — Dans sa configuration de base, SSL permet d'authentifier le serveur et de sécuriser la communication. Ainsi, un assaillant ne peut pas intercepter la communication – ou se faire passer pour le serveur. Par contre rien ne garantit l'identité du client.


Très bien, nous pouvons nous connecter en utilisant SSL. Mais rien ne nous empêche pour l'instant de nous connecter sans! C'est à dire, comme au début de cet article, en faisant passer les données en clair. Il est parfois nécessaire de n'accepter que des communications sécurisées. Dans MySQL il est possible d'exiger une communication cryptée lorsque l'on fixe les permissions pour un utilisateur à l'aide de la clause REQUIRE SSL de la commande SQL GRANT:

sh$ mysql -u root -p
Enter password: 
mysql> GRANT SELECT ON MyDVDRental.CheckedOut TO audit@'%' 
                    IDENTIFIED BY 'password1234' 
                    REQUIRE SSL;
Query OK, 0 rows affected (0.00 sec)
sh$ mysql -u audit -h chicoree -p  MyDVDRental
Enter password: 
ERROR 1045 (28000): Access denied for user 'audit'@'localhost' (using password: YES)
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem 
Enter password: 
mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.00 sec)

Authentification mutuelle

Authentifier le serveur et exiger une communication cryptée est déjà un bon début. Mais il est parfois aussi nécessaire de vérifier l'identité du client. C'est à dire de faire l'authentification mutuelle du client et du serveur. Dans ce cadre, MySQL propose diverses solutions pour s'assurer avec plus ou moins de force de l'identité du client que nous allons maintenant détailler.

SSL authentification mutuelle.png

Authentification mutuelle — Avec l'authentification mutuelle SSL permet de garantir l'identité du client et du serveur et de sécuriser la communication.


Forcer l'utilisation d'un certificat X.509

La première manière de procéder va être d'exiger de la part du client un certificat X.509:

sh$ mysql -u root -p
Enter password: 
mysql> GRANT SELECT ON MyDVDRental.CheckedOut TO audit@'%' 
                    IDENTIFIED BY 'password1234' 
                    REQUIRE X509;
Query OK, 0 rows affected (0.00 sec)

Attention:

Notez qu'avec cette configuration, les informations contenues dans le certificat n'ont aucune importance! Tout ce qui compte c'est que le certificat soit signé par une autorité reconnue par le serveur...

Pour pouvoir se connecter, le client va maintenant devoir fournir un certificat (signé par un autorité reconnue par le serveur):

sh$ mysql -u audit -h chicoree -p  MyDVDRental
Enter password: 
ERROR 1045 (28000): Access denied for user 'audit'@'localhost' (using password: YES)
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem 
Enter password: 
ERROR 1045 (28000): Access denied for user 'audit'@'localhost' (using password: YES)
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem
Enter password: 
SSL error: Unable to get private key from 'client-cert.pem'
ERROR 2026 (HY000): SSL connection error
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem 
Enter password: 
mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.00 sec)

Forcer la vérification du sujet du certificat (SUBJECT)

Dans la version précédente, le simple fait de fournir un certificat signé par une autorité reconnue par le serveur permet d'authentifier le client. Mais jusqu'à présent les informations contenues dans le certificat n'avaient pas eu d'importance.

Cela change avec la clause REQUIRE SUBJECT de la requête SQL GRANT. Celle-ci permet d'exiger une certaine valeur dans le champ subject du certificat. Ce qui en pratique limite l'accès uniquement aux sujets (utilisateurs, machines, processus, etc.) dont l'autorité de certification a accepté de garantir l'identité:

sh$ mysql -u root -p'
Enter password: 
mysql> GRANT SELECT ON MyDVDRental.CheckedOut TO audit@'%' 
                    IDENTIFIED BY 'password1234' 
                    REQUIRE SUBJECT '/C=FR/ST=France/O=chicoree/CN=sylvain';
Query OK, 0 rows affected (0.00 sec)
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem 
Enter password: 
mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.00 sec)

Forcer la vérification de l'émetteur du certificat (ISSUER)

Enfin, il est aussi possible d'exiger que le certificat fourni par le client soit émis (signé) par une certaine autorité:

sh$ mysql -u root -p'
Enter password: 
mysql> GRANT SELECT ON MyDVDRental.CheckedOut TO audit@'%' 
                    IDENTIFIED BY 'password1234' 
                    REQUIRE ISSUER '/C=FR/ST=France/O=chicoree/CN=sylvain';
Query OK, 0 rows affected (0.00 sec)
sh$ mysql -u audit -h chicoree -p  MyDVDRental --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem 
Enter password: 
mysql> SELECT * FROM CheckedOut;
+--------------------+------------+
| title              | client     |
+--------------------+------------+
| Gone with the Wind | John DOE   | 
| My Fair Lady       | Rock SMITH | 
| Rambo II           | John DOE   | 
+--------------------+------------+
3 rows in set (0.00 sec)

Combinaisons

Pour terminer il faut signaler que les clauses REQUIRE ISSUER et REQUIRE SUBJECT peuvent être combinées. Par ailleurs, il existe aussi une clause REQUIRE CIPHER qui permet d'exiger une certaine force de chiffrement, et qui peut être utilisée seule ou combinée aux deux clauses mentionnées ci-dessus. Je vous invite à vous reporter à la documentation de MySQL pour en savoir plus.

Conclusion

Un élément aussi critique qu'un serveur de base de données mérite bien qu'on investisse un peu de temps pour le sécuriser. Ici, nous avons parlé exclusivement de SSL, mais ne vous y trompez pas: "passer le serveur en SSL" ne suffit pas à le sécuriser! Si votre serveur est par ailleurs fragile, SSL ne vous "sauvera pas la mise". C'est juste un élément de plus dans l'arsenal de la sécurisation de votre serveur de bases de données. Mais un élément important!

Références