From 35d3ab5f345efcb836ace6d3ff845d1785849c09 Mon Sep 17 00:00:00 2001 From: Francois Lesueur Date: Thu, 24 Mar 2022 08:31:23 +0100 Subject: [PATCH] tp5 bd --- tp5-bd.md | 139 ++++++++++++++++++++ tp5-files/docker-compose.yml | 37 ++++++ tp5-files/sqlinit/sqlinit.sql | 11 ++ tp5-files/webapp/Dockerfile | 3 + tp5-files/webapp/README.txt | 13 ++ tp5-files/webapp/admin/do_login.php | 19 +++ tp5-files/webapp/admin/do_support.php | 32 +++++ tp5-files/webapp/admin/index.php | 17 +++ tp5-files/webapp/admin/print_clients.php | 45 +++++++ tp5-files/webapp/clients/do_login.php | 19 +++ tp5-files/webapp/clients/do_message.php | 19 +++ tp5-files/webapp/clients/index.php | 17 +++ tp5-files/webapp/clients/print_clients.php | 37 ++++++ tp5-files/webapp/db.inc.php | 9 ++ tp5-files/webapp/helpdesk/do_helpdesk.php | 24 ++++ tp5-files/webapp/helpdesk/do_login.php | 18 +++ tp5-files/webapp/helpdesk/index.php | 17 +++ tp5-files/webapp/helpdesk/print_clients.php | 33 +++++ 18 files changed, 509 insertions(+) create mode 100644 tp5-bd.md create mode 100644 tp5-files/docker-compose.yml create mode 100644 tp5-files/sqlinit/sqlinit.sql create mode 100755 tp5-files/webapp/Dockerfile create mode 100755 tp5-files/webapp/README.txt create mode 100755 tp5-files/webapp/admin/do_login.php create mode 100755 tp5-files/webapp/admin/do_support.php create mode 100755 tp5-files/webapp/admin/index.php create mode 100755 tp5-files/webapp/admin/print_clients.php create mode 100755 tp5-files/webapp/clients/do_login.php create mode 100755 tp5-files/webapp/clients/do_message.php create mode 100755 tp5-files/webapp/clients/index.php create mode 100755 tp5-files/webapp/clients/print_clients.php create mode 100755 tp5-files/webapp/db.inc.php create mode 100755 tp5-files/webapp/helpdesk/do_helpdesk.php create mode 100755 tp5-files/webapp/helpdesk/do_login.php create mode 100755 tp5-files/webapp/helpdesk/index.php create mode 100755 tp5-files/webapp/helpdesk/print_clients.php diff --git a/tp5-bd.md b/tp5-bd.md new file mode 100644 index 0000000..de7b1a0 --- /dev/null +++ b/tp5-bd.md @@ -0,0 +1,139 @@ +# TP 5 : Sécurité des bases de données (12h) + +Pour ce TP, nous allons utiliser Docker. Vous pouvez l'installer sur votre machine ou dans une VM quelconque. + +Ce TP peut être réalisé dans la VM MI-LXC disponible [ici](https://flesueur.irisa.fr/mi-lxc/images/milxc-debian-amd64-1.4.1.ova) (identique à la première période). Avant de lancer la VM, il peut être nécessaire de diminuer la RAM allouée. Par défaut, la VM a 3GO : si vous avez 4GO sur votre machine physique, il vaut mieux diminuer à 2GO, voire 1.5GO pour la VM (la VM devrait fonctionner de manière correcte toujours). + + +Quelques commandes utiles +========================= + +Dans la console psql : +* \l lister les bases +* \dt lister les tables +* \du (\du+) lister les rôles (utilisateurs/groupes) +* \z lister les permissions +* TABLE clients; afficher la table clients +* \c database; ouvrir la base database +* La doc de référence : [doc](https://www.postgresql.org/docs/current/index.html) + +Démarrage (30 minutes) +========= + +Ici, on va démarrer une base PostgreSQL et faire de premières manipulations. Pour créer et démarrer un docker postgresql : + +`docker run --name mypostgres -e POSTGRES_PASSWORD=foo -d postgres` + +Ensuite, pour se connecter à la console : + +`docker exec -it mypostgres psql -U postgres` + +La base est persistente si vous stoppez/redémarrez le docker, mais pas en cas de fausse manip. Prenez des notes au fur et à mesure ! + + +Création d'une première base (2 heures) +============================ + +Il faut tout d'abord comprendre la notion de rôles (utilisateurs/groupes) et de bases. + +Rôles +----- + +La documentation est à lire [ici](https://www.postgresql.org/docs/current/user-manag.html) (jusque 22.2 seulement pour l'instant). Les groupes et les utilisateurs sont des rôles avec des attributs différents. La modification d'un rôle est expliquée [ici](https://www.postgresql.org/docs/current/sql-alterrole.html). + +> Créez un utilisateur ayant le droit de se connecter, avec un mot de passe. Puis utilisez ALTER pour changer son mot de passe. + +Base +---- + +Une base est un ensemble de tables (relations) qui appartient à un utilisateur. La création de base est documentée [ici](https://www.postgresql.org/docs/current/managing-databases.html) (ignorez les parties templates et tablespaces). + +> Créez une nouvelle base qui doit appartenir à l'utilisateur que vous avez créé. Puis supprimez-là, récréez-là et utilisez ALTER pour modifier (puis remettre) son propriétaire. + + +Tables +------ + +Les tables sont les éléments des bases qui vont contenir les données qui seront requêtées. Pour créer une table, la [doc](https://www.postgresql.org/docs/current/ddl-basics.html). Il faut ensuite la remplir avec [INSERT/UPDATE/DELETE](https://www.postgresql.org/docs/current/dml.html) et la requêter avec [SELECT](https://www.postgresql.org/docs/current/queries.html). + +> Créez une table, insérez et modifiez quelques lignes et requêtez-là avec un WHERE simple. + +Nous avons maintenant le nécessaire pour explorer les fonctionnalités de sécurité. + +Disponibilité (30 minutes) +============= + +Expérimentez le dump et la restauration tel que décrit [ici](https://www.postgresql.org/docs/current/backup-dump.html) + +Pour vous connecter en shell sur le docker, `docker exec -it mypostgres bash` (où bash peut évidemment être remplacé par toute commande que vous vouliez taper dans le docker). + +> Présentez votre déroulé. + +Contrôle d'accès (3 heures) +================ + +Pour la confidentialité et l'intégrité, nous allons voir la gestion des comptes et des droits. + +<< PAUSE COURS AU TABLEAU >> + +Gestion des comptes +------------------- + +Il est maintenant temps de lire la gestion de l'appartenance aux rôles [ici](https://www.postgresql.org/docs/current/role-membership.html). + +> Créez quelques utilisateurs, quelques groupes et affectez des utilisateurs aux groupes. + +Gestion des droits +------------------ + +PostgreSQL permet de régler les droits : +* des tables : [ici](https://www.postgresql.org/docs/current/ddl-priv.html) +* des lignes : [ici](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) + +La gestion par tables permet la première approche à grain moyen, la gestion par lignes permet ensuite une granularité de contrôle beaucoup plus fine. Les droits sont affectés à des rôles afin de faire du RBAC. + +> Sur une table, permettez à un rôle de lire uniquement (SELECT) et à un autre d'écrire. + +> Sur des lignes, limitez la lecture à l'utilisateur nommé sur la ligne. + +Mise en œuvre (6 heures) +============= + +Bravo ! Vous voilà maintenant responsable de mettre une formidable application en production ! Cette application a été développée avec les dernières technologies à la mode par le stagiaire qui vient de partir (parce que c'était, avant vous, le seul technique de l'organisation). + +Cette application est disponible dans le sous-dossier [tp5-files](tp5-files/). Vous pouvez la lancer avec `docker-compose up -d` (en étant placé dans le sous-dossier contenant le docker-compose.yml) et y accéder depuis votre hôte aux URLs `http://localhost/admin/`, `http://localhost/clients/` et `http://localhost/helpdesk/`. + +Vous pouvez explorer son code (et son README.txt) dans le sous-dossier [webapp](tp5-files/webapp/). La base initiale est décrite dans [sqlinit.sql](tp5-files/sqlinit/sqlinit.sql), ce qui vous permet de connaître les authentifiants attendus. Pour vous connecter à la base (credentials www/foo) : +* `docker exec -it postgres psql -U www clientsdb` +* ou via un navigateur vers `http://localhost:81` (phppgadmin) + +Et là, c'est le drame. En regardant `clients/do_login.php`, vous prenez peur pour la mise en production. + +> Quel est le problème dans ce fichier ? Retrouvez-vous ce problème ailleurs ? Qu'aurait-il fallu faire à la place ? Exploitez-le ! + +Malheureusement, la recette a déjà eu lieu et vous n'avez plus la possibilité de faire des modifications au travers de toute l'application. Nous allons explorer 2 voies de défense en profondeur afin de limiter les impacts : +* Séparation des accès à la BD en 3 utilisateurs distincts admin/helpdesk/client puis restriction des droits sur les tables +* Séparation des accès à la BD en n utilisateurs distincts appartenant à l'un des 3 rôles puis application de Row-Level security + +Segmentation des accès en 3 utilisateurs +---------------------------------------- + +Raffinez l'authentification entre l'application et la BD afin de limiter les dégâts potentiels : +* Créez 3 utilisateurs distincts admin/helpdesk/client au niveau de la BD, qui correspondront aux usages des 3 sous-dossiers de l'application +* Attribuez leur les droits minimaux nécessaires à chacune des sections de l'application (droits de SELECT, INSERT et UPDATE sur la table clients) +* Modifiez l'inclusion du `db.inc.php` pour que les fichiers PHP de chaque sous-dossier se connectent à la base avec les credentials adaptés + +> Déployez ce modèle et mettez à jour le code PHP en fonction. Vérifiez que certaines exploitations initiales ne fonctionnent plus. + +Segmentation des accès en n utilisateurs +---------------------------------------- + +Raffinez l'authentification entre l'application et la BD afin de limiter les dégâts potentiels : +* Un utilisateur de l'application web = un utilisateur de la BD +* L'authentification sera réalisée directement avec la BD (stockages des authentifiants dans la session PHP) +* Des rôles (hiérarchiques) pour factoriser la gestion des droits +* Une sécurité à grain fin au niveau des lignes de tables (Row security) + +> Proposez (sur papier) et faîtes valider un modèle RBAC adapté. Déployez ce modèle et mettez à jour le code PHP en fonction. Vérifiez que votre exploitation initiale ne fonctionne plus. + +> REMARQUE : En l'absence de row-level security (pas disponible avec MySQL/MariaDB par exemple), un résultat relativement similaire (mais plus complexe à maintenir) aurait pu être obtenu avec l'utilisation de vues. diff --git a/tp5-files/docker-compose.yml b/tp5-files/docker-compose.yml new file mode 100644 index 0000000..007d287 --- /dev/null +++ b/tp5-files/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.3" + +services: + db: + image: postgres + container_name: postgres + #networks: + # - postgres + environment: + - POSTGRES_DB=clientsdb + - POSTGRES_PASSWORD=foo + - POSTGRES_USER=www + ports: + - 5432:5432 + volumes: + - ${PWD}/sqlinit:/docker-entrypoint-initdb.d + - mydb:/var/lib/postgresql/data + + webapp: + build: webapp/ + image: webapp-image + container_name: webapp + ports: + - 80:80 + volumes: + - ${PWD}/webapp:/var/www/html + + phppgadmin: + image: bitnami/phppgadmin + container_name: phppgadmin + ports: + - 81:8080 + environment: + - DATABASE_HOST=postgres + +volumes: + mydb: diff --git a/tp5-files/sqlinit/sqlinit.sql b/tp5-files/sqlinit/sqlinit.sql new file mode 100644 index 0000000..e7fd992 --- /dev/null +++ b/tp5-files/sqlinit/sqlinit.sql @@ -0,0 +1,11 @@ +-- CREATE USER www WITH PASSWORD 'foo'; +-- CREATE DATABASE clientsdb OWNER www; +\c clientsdb +CREATE TABLE clients (id SERIAL, name varchar(15), password varchar(15), role varchar(15), email varchar(25), comment varchar(5000), message varchar(5000)); +ALTER TABLE clients OWNER TO www; +INSERT INTO clients( name, password, role) VALUES ('admin', 'admin', 'admin'); +INSERT INTO clients( name, password, role) VALUES ('helpdesk1', 'helpdesk1', 'helpdesk'); +INSERT INTO clients( name, password, role) VALUES ('helpdesk2', 'helpdesk2', 'helpdesk'); +INSERT INTO clients( name, password, role) VALUES ('client1', 'client1', 'client'); +INSERT INTO clients( name, password, role) VALUES ('client2', 'client2', 'client'); +INSERT INTO clients( name, password, role) VALUES ('client3', 'client3', 'client'); diff --git a/tp5-files/webapp/Dockerfile b/tp5-files/webapp/Dockerfile new file mode 100755 index 0000000..a2dd269 --- /dev/null +++ b/tp5-files/webapp/Dockerfile @@ -0,0 +1,3 @@ +FROM php:apache + +RUN apt-get update && apt-get install -y libpq-dev && docker-php-ext-install pgsql diff --git a/tp5-files/webapp/README.txt b/tp5-files/webapp/README.txt new file mode 100755 index 0000000..639e936 --- /dev/null +++ b/tp5-files/webapp/README.txt @@ -0,0 +1,13 @@ +Application web de gestion clientèle. + +- admin/ contient le code pour ajouter/supprimer des clients +- helpdesk/ contient le code pour modifier des clients, lire leur message et le modifier +- clients/ contient le code permettant à chaque client de voir ses infos et d'envoyer un message au helpdesk + +Dans chaque dossier : +- index.php fournit la page de login +- do_login.php traite le login, stocke login et pass dans la session PHP puis redirige vers la page d'affichage print_clients.php +- print_clients.php affiche, selon le cas, la liste + ajout/suppression, la liste + modification ou le compte du client connecté +- do_*.php traite les actions sur la BD (insertion, suppression, modification) demandées par print_clients.php puis redirige vers print_clients.php + +La table utilisée est 'clients' de la base 'clientsdb' avec le compte www/foo. diff --git a/tp5-files/webapp/admin/do_login.php b/tp5-files/webapp/admin/do_login.php new file mode 100755 index 0000000..78c28e0 --- /dev/null +++ b/tp5-files/webapp/admin/do_login.php @@ -0,0 +1,19 @@ + 0) + header('Location: print_clients.php'); +else + print "Authentication failed, $query"; +?> diff --git a/tp5-files/webapp/admin/do_support.php b/tp5-files/webapp/admin/do_support.php new file mode 100755 index 0000000..aa6c7f2 --- /dev/null +++ b/tp5-files/webapp/admin/do_support.php @@ -0,0 +1,32 @@ +Logout

"; + +require_once("../db.inc.php"); + + +// Gestion des ajouts/suppressions +if (isset($_POST['name'])) { // Ajout d'un nouveau client + $name=$_POST['name']; + $email=$_POST['email']; + $comment=$_POST['comment']; + $password=$_POST['password']; + $role=$_POST['role']; + $query = "INSERT INTO clients (name, email, comment, password, role) VALUES ('$name','$email','$comment', '$password', '$role')"; + pg_query($dbconn, $query) or die('Échec de la requête : ' . pg_last_error()); +} + +if (isset($_GET['delete'])) { // Suppression d'un client + $id = $_GET['delete']; + $query = "DELETE FROM clients WHERE id='$id'"; + pg_query($dbconn, $query) or die('Échec de la requête : ' . pg_last_error()); +} + + +// Ferme la connexion +pg_close($dbconn); + +header('Location: print_clients.php'); +?> diff --git a/tp5-files/webapp/admin/index.php b/tp5-files/webapp/admin/index.php new file mode 100755 index 0000000..23b399a --- /dev/null +++ b/tp5-files/webapp/admin/index.php @@ -0,0 +1,17 @@ + + +Login Admin + +
+Login:
+Password:
+ +
+ + + + diff --git a/tp5-files/webapp/admin/print_clients.php b/tp5-files/webapp/admin/print_clients.php new file mode 100755 index 0000000..3694efa --- /dev/null +++ b/tp5-files/webapp/admin/print_clients.php @@ -0,0 +1,45 @@ +Logout

"; + +require_once("../db.inc.php"); + +// Affichage de la table des clients +// Exécution de la requête SQL +$query = 'SELECT * FROM clients'; +$result = pg_query($dbconn, $query) or die('Échec de la requête : ' . pg_last_error()); + +// Affichage des résultats en HTML +echo "\n"; +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $id=$line['id']; + $name=$line['name']; + $email=$line['email']; + $comment=$line['comment']; + $role=$line['role']; + + echo "\t\n"; + echo "\t\n"; +} +echo "
NameEmailCommentRoleSupprimer
$name$email$comment$roleSupprimer
\n"; + +// Libère le résultat +pg_free_result($result); + +// Ferme la connexion +pg_close($dbconn); +?> + + + +

+Ajouter un client :
+
+Name:
+Password:
+Role:
+Email:
+Comment:
+ +
diff --git a/tp5-files/webapp/clients/do_login.php b/tp5-files/webapp/clients/do_login.php new file mode 100755 index 0000000..d273572 --- /dev/null +++ b/tp5-files/webapp/clients/do_login.php @@ -0,0 +1,19 @@ + 0) + header('Location: print_clients.php'); +else + print "Authentication failed, $query"; +?> diff --git a/tp5-files/webapp/clients/do_message.php b/tp5-files/webapp/clients/do_message.php new file mode 100755 index 0000000..ae53c5b --- /dev/null +++ b/tp5-files/webapp/clients/do_message.php @@ -0,0 +1,19 @@ + diff --git a/tp5-files/webapp/clients/index.php b/tp5-files/webapp/clients/index.php new file mode 100755 index 0000000..ef3b3b0 --- /dev/null +++ b/tp5-files/webapp/clients/index.php @@ -0,0 +1,17 @@ + + +Login Client + +
+Login:
+Password:
+ +
+ + + + diff --git a/tp5-files/webapp/clients/print_clients.php b/tp5-files/webapp/clients/print_clients.php new file mode 100755 index 0000000..6e1ebbb --- /dev/null +++ b/tp5-files/webapp/clients/print_clients.php @@ -0,0 +1,37 @@ +Logout

"; + +require_once("../db.inc.php"); + +// Affichage de la table des clients +// Exécution de la requête SQL +$login = $_SESSION['login']; +$query = "SELECT * FROM clients WHERE name='$login'"; +$result = pg_query($dbconn,$query) or die('Échec de la requête : ' . pg_last_error()); + +// Affichage des résultats en HTML +echo "\n"; +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $id=$line['id']; + $name=$line['name']; + $email=$line['email']; + $message=$line['message']; + + echo "\t\n"; +} +echo "
NameEmailMessage
$name$email$message
\n"; + +// Libère le résultat +pg_free_result($result); + +// Ferme la connexion +pg_close($dbconn); +?> + +Envoyer un message : +
+ + +
diff --git a/tp5-files/webapp/db.inc.php b/tp5-files/webapp/db.inc.php new file mode 100755 index 0000000..c7013f0 --- /dev/null +++ b/tp5-files/webapp/db.inc.php @@ -0,0 +1,9 @@ + diff --git a/tp5-files/webapp/helpdesk/do_helpdesk.php b/tp5-files/webapp/helpdesk/do_helpdesk.php new file mode 100755 index 0000000..e4a494c --- /dev/null +++ b/tp5-files/webapp/helpdesk/do_helpdesk.php @@ -0,0 +1,24 @@ +Logout

"; + +require_once("../db.inc.php"); + +// Gestion des modifications +if (isset($_POST['name'])) { // Modification d'un client existant + $name=$_POST['name']; + $email=$_POST['email']; + $comment=$_POST['comment']; + $id=$_POST['id']; + $message=$_POST['message']; + $query = "UPDATE clients SET name='$name',email='$email',comment='$comment',message='$message' WHERE id='$id'"; + pg_query($dbconn, $query) or die('Échec de la requête : ' . pg_last_error()); +} + +// Ferme la connexion +pg_close($dbconn); + +header('Location: print_clients.php'); +?> diff --git a/tp5-files/webapp/helpdesk/do_login.php b/tp5-files/webapp/helpdesk/do_login.php new file mode 100755 index 0000000..fd30d64 --- /dev/null +++ b/tp5-files/webapp/helpdesk/do_login.php @@ -0,0 +1,18 @@ + 0) + header('Location: print_clients.php'); +else + print "Authentication failed, $query"; +?> diff --git a/tp5-files/webapp/helpdesk/index.php b/tp5-files/webapp/helpdesk/index.php new file mode 100755 index 0000000..0dfe467 --- /dev/null +++ b/tp5-files/webapp/helpdesk/index.php @@ -0,0 +1,17 @@ + + +Login Helpdesk + +
+Login:
+Password:
+ +
+ + + + diff --git a/tp5-files/webapp/helpdesk/print_clients.php b/tp5-files/webapp/helpdesk/print_clients.php new file mode 100755 index 0000000..6992568 --- /dev/null +++ b/tp5-files/webapp/helpdesk/print_clients.php @@ -0,0 +1,33 @@ +Logout

"; + +require_once("../db.inc.php"); + +// Affichage de la table des clients +// Exécution de la requête SQL +$query = 'SELECT * FROM clients'; +$result = pg_query($dbconn, $query) or die('Échec de la requête : ' . pg_last_error()); + +// Affichage des résultats en HTML +echo "\n"; +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $id=$line['id']; + $name=$line['name']; + $email=$line['email']; + $comment=$line['comment']; + $message=$line['message']; + + echo "\t\n"; + +echo "\t\n"; +} +echo "
NameEmailCommentMessageNameEmailCommentMessageUpdate
$name$email$comment $message
\n"; + +// Libère le résultat +pg_free_result($result); + +// Ferme la connexion +pg_close($dbconn); +?>