Skip to main content

Sécurité des bases de données

Introduction

Les bases de données contiennent souvent les informations les plus sensibles d'une organisation : données personnelles, mots de passe, informations financières. Une faille de sécurité peut avoir des conséquences graves :

  • Fuite de données : vol d'informations confidentielles
  • Modification de données : altération malveillante
  • Suppression de données : perte irréversible
  • Non-conformité légale : sanctions RGPD

La sécurité repose sur trois axes : le réseau, les accès, et l'application.

Réduire la surface d'exposition

La surface d'exposition représente l'ensemble des points d'entrée potentiels pour un attaquant. L'objectif est de la réduire au minimum.

Bind address

Par défaut, configurez le serveur pour n'écouter qu'en local :

# /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 127.0.0.1

N'utilisez 0.0.0.0 (toutes les interfaces) que si des clients externes doivent se connecter, et uniquement combiné avec un firewall.

Firewall

Limitez les IP autorisées à se connecter au port de la base de données :

# Autoriser uniquement le serveur web (192.168.1.10) à accéder à MariaDB
sudo ufw allow from 192.168.1.10 to any port 3306
sudo ufw deny 3306

Ne jamais exposer directement sur Internet

Une base de données ne devrait jamais être accessible directement depuis Internet. Pour un accès distant, utilisez :

  • Un tunnel SSH
  • Un VPN
  • Un bastion host
# Exemple : tunnel SSH pour accéder à la base distante
ssh -L 3306:localhost:3306 user@serveur-distant

# Puis connexion locale
mysql -h 127.0.0.1 -u utilisateur -p

Gestion des utilisateurs et privilèges

Principe du moindre privilège

Chaque utilisateur ne doit avoir que les droits strictement nécessaires à sa fonction.

-- À ÉVITER : donner tous les droits
GRANT ALL PRIVILEGES ON *.* TO 'mon_app'@'%';

-- PRÉFÉRER : droits limités à une base et aux opérations nécessaires
GRANT SELECT, INSERT, UPDATE, DELETE ON starwars.* TO 'mon_app'@'192.168.1.10';

Utilisateurs dédiés par application

Créez un utilisateur distinct pour chaque application :

-- Utilisateur pour l'application web (lecture/écriture)
CREATE USER 'app_web'@'192.168.1.10' IDENTIFIED BY 'MotDePasse_Complexe!2024';
GRANT SELECT, INSERT, UPDATE, DELETE ON starwars.* TO 'app_web'@'192.168.1.10';

-- Utilisateur pour les rapports (lecture seule)
CREATE USER 'app_rapports'@'192.168.1.20' IDENTIFIED BY 'Autre_MotDePasse!2024';
GRANT SELECT ON starwars.* TO 'app_rapports'@'192.168.1.20';

-- Appliquer les changements
FLUSH PRIVILEGES;

Ne jamais utiliser root pour les applications

Le compte root ne doit servir qu'à l'administration. Une application compromise avec un accès root = base de données compromise.

Authentification

Mots de passe forts

Utilisez des mots de passe longs et complexes :

  • Minimum 16 caractères
  • Mélange de majuscules, minuscules, chiffres, caractères spéciaux
  • Uniques pour chaque utilisateur

mysql_secure_installation

Après l'installation de MariaDB, exécutez cette commande :

sudo mysql_secure_installation

Elle permet de :

  • Définir un mot de passe root
  • Supprimer les utilisateurs anonymes
  • Désactiver la connexion root à distance
  • Supprimer la base de test

Cette commande devrait être exécutée systématiquement sur tout nouveau serveur MariaDB.

Injection SQL

L'injection SQL est l'une des vulnérabilités les plus courantes et les plus dangereuses. Elle figure dans le top 10 OWASP depuis des années.

Principe

L'attaque consiste à insérer du code SQL malveillant dans une entrée utilisateur qui sera exécutée par la base de données.

Exemple vulnérable - recherche d'un personnage par nom :

-- L'utilisateur entre : Vader
SELECT * FROM personnages WHERE nom = 'Vader';
-- Résultat : Dark Vador est retourné ✓

-- L'utilisateur malveillant entre : ' OR '1'='1
SELECT * FROM personnages WHERE nom = '' OR '1'='1';
-- Résultat : TOUS les personnages sont retournés ✗

-- Pire, l'utilisateur entre : '; DROP TABLE personnages; --
SELECT * FROM personnages WHERE nom = ''; DROP TABLE personnages; --';
-- Résultat : la table est supprimée ✗

Protection : les requêtes préparées

La solution est d'utiliser des requêtes préparées (prepared statements) qui séparent le code SQL des données.

PHP avec PDO :

// ❌ VULNÉRABLE - Ne jamais faire ça
$nom = $_GET['nom'];
$sql = "SELECT * FROM personnages WHERE nom = '$nom'";
$result = $pdo->query($sql);

// ✅ SÉCURISÉ - Requête préparée
$nom = $_GET['nom'];
$stmt = $pdo->prepare("SELECT * FROM personnages WHERE nom = ?");
$stmt->execute([$nom]);
$result = $stmt->fetchAll();

Python avec MySQL Connector :

# ❌ VULNÉRABLE - Ne jamais faire ça
nom = input("Nom du personnage: ")
cursor.execute(f"SELECT * FROM personnages WHERE nom = '{ nom }'")

# ✅ SÉCURISÉ - Requête préparée
nom = input("Nom du personnage: ")
cursor.execute("SELECT * FROM personnages WHERE nom = %s", (nom,))

Ne jamais concaténer des entrées utilisateur dans une requête SQL, même après validation. Utilisez toujours des requêtes préparées.

Bonnes pratiques - Résumé

Axe Action
Réseau Bind sur 127.0.0.1, firewall, pas d'exposition Internet directe
Accès Moindre privilège, utilisateurs dédiés, mots de passe forts
Authentification Exécuter mysql_secure_installation, désactiver root distant
Application Requêtes préparées systématiques, jamais de concaténation