Polar Code 🎭

Command Palette

Search for a command to run...

07
Pièce N°07

SCENE 7 : Les Vues et la Sécurité - Les Murs et les Miroirs

Certains murs sont transparents. D'autres, aveugles. Les vues, ce sont des fenêtres taillées dans la réalité. La sécurité, ce sont les serrures sur ces fenêtres. Dans cette ville, on ne montre pas tout à tout le monde.

Les Vues – Des Fenêtres sur la Réalité

Une vue, c'est une requête SQL sauvegardée sous un nom.
Une table virtuelle. Un reflet.
Vous ne stockez pas les données, vous stockez la façon de les regarder.

Pourquoi des Vues ? L'Art du Leurre

  1. Simplification – Cacher la complexité. Une jointure à 5 tables devient SELECT * FROM vue_dashboard.
  2. Sécurité – Montrer seulement certaines colonnes, certaines lignes.
  3. Cohérence – Une logique métier centralisée. Si elle change, on change la vue, pas 100 requêtes.
  4. Compatibilité – Garder une interface stable même si les tables changent en dessous.

CREATE VIEW – Tailler la Fenêtre

-- Vue simple : les clients actifs
CREATE VIEW vue_clients_actifs AS
SELECT client_id, nom, prenom, email, ville
FROM client
WHERE actif = true
  AND date_derniere_commande > CURRENT_DATE - INTERVAL '1 year';

-- Utilisation : comme une table
SELECT * FROM vue_clients_actifs WHERE ville = 'Paris';

Vues avec Jointures – Les Panoramas

-- Vue pour le dashboard commercial
CREATE VIEW vue_commandes_detaillees AS
SELECT 
    c.commande_id,
    c.date_commande,
    c.montant_total,
    cl.nom AS client_nom,
    cl.ville,
    e.nom AS employe_nom,
    COUNT(lc.produit_id) AS nb_produits
FROM commande c
JOIN client cl ON c.client_id = cl.client_id
LEFT JOIN employe e ON c.commercial_id = e.employe_id
LEFT JOIN ligne_de_commande lc ON c.commande_id = lc.commande_id
GROUP BY c.commande_id, cl.nom, cl.ville, e.nom;
-- Maintenant, le commercial voit tout d'un coup.

Vues Matérialisées – Le Reflet Figé

Une vue normale recalcul tout à chaque requête.
Une vue matérialisée stocke physiquement le résultat.
Comme une photo. Rapide à consulter, mais il faut la rafraîchir.

-- Pour un rapport quotidien lourd
CREATE MATERIALIZED VIEW mv_stats_quotidiennes AS
SELECT 
    date_commande::DATE as jour,
    COUNT(*) as nb_commandes,
    SUM(montant_total) as ca_total,
    AVG(montant_total) as panier_moyen
FROM commande
WHERE date_commande > CURRENT_DATE - INTERVAL '30 days'
GROUP BY date_commande::DATE
WITH DATA; -- Remplit immédiatement

-- Consultation ultra-rapide
SELECT * FROM mv_stats_quotidiennes ORDER BY jour DESC;

-- Rafraîchissement (à faire périodiquement)
REFRESH MATERIALIZED VIEW mv_stats_quotidiennes;
-- Coûteux. À faire la nuit.

WITH CHECK OPTION – La Vitre Teintée

Pour les vues updatables, cette option empêche d'insérer/modifier des lignes qui ne seraient pas visibles par la vue.

CREATE VIEW vue_clients_parisiens AS
SELECT * FROM client WHERE ville = 'Paris'
WITH CHECK OPTION;

-- Ça marche :
UPDATE vue_clients_parisiens SET email = 'nouveau@paris.fr' WHERE client_id = 42;

-- Ça échoue (le client deviendrait invisible) :
UPDATE vue_clients_parisiens SET ville = 'Lyon' WHERE client_id = 42;
-- "row violates check option for view"

La Sécurité – Les Serrures et les Clés

Dans cette ville, il y a des quartiers interdits.
Des rues qu'on ne montre pas aux touristes.
La sécurité SQL, c'est définir qui a le droit de faire quoi.

Les Rôles et les Utilisateurs – Les Cartes d'Identité

PostgreSQL : Pas de différence conceptuelle entre un rôle et un utilisateur. Un utilisateur est un rôle avec LOGIN.
MySQL : USER pour se connecter, ROLE pour regrouper des permissions.

-- Créer un rôle (PostgreSQL)
CREATE ROLE lecteur_ventes;
CREATE ROLE redacteur_ventes;
CREATE USER pierre WITH PASSWORD 'motdepassecomplexe';

-- MySQL
CREATE USER 'pierre'@'localhost' IDENTIFIED BY 'motdepassecomplexe';
CREATE ROLE 'redacteur_ventes';

GRANT – Donner les Clés

La commande qui ouvre les portes.
Précise, chirurgicale.

-- Donner le droit de SELECT sur une vue
GRANT SELECT ON vue_commandes_detaillees TO lecteur_ventes;

-- Donner plusieurs droits sur une table
GRANT SELECT, INSERT, UPDATE ON client TO redacteur_ventes;

-- Donner tous les droits (attention !)
GRANT ALL PRIVILEGES ON commande TO administrateur;

-- Donner le droit de donner les droits (encore plus dangereux)
GRANT SELECT ON client TO pierre WITH GRANT OPTION;
-- Maintenant pierre peut donner ce droit à d'autres.

REVOKE – Reprendre les Clés

Quand quelqu'un sort du jeu.
Quand une porte doit se refermer.

-- Retirer un droit spécifique
REVOKE INSERT ON client FROM redacteur_ventes;

-- Retirer tous les droits
REVOKE ALL PRIVILEGES ON commande FROM ancien_employe;

-- Retirer l'option GRANT
REVOKE GRANT OPTION FOR SELECT ON client FROM pierre;

Les Niveaux de Privilèges – Les Cercles de Confiance

  1. Niveau base de donnéesCREATE DATABASE, CONNECT.
  2. Niveau schémaCREATE, USAGE sur un schéma.
  3. Niveau table/vueSELECT, INSERT, UPDATE, DELETE, REFERENCES, TRIGGER.
  4. Niveau colonneUPDATE(nom_colonne), SELECT(nom_colonne).
  5. Niveau routineEXECUTE sur les fonctions.
-- Autoriser seulement sur certaines colonnes
GRANT SELECT (nom, prenom, email) ON client TO support;

-- Le support ne verra pas l'adresse, le téléphone, le salaire.
-- Une fenêtre étroite.

Les Rôles Hérités – La Hiérarchie

-- PostgreSQL
CREATE ROLE employe_base;
GRANT SELECT ON vue_clients_actifs TO employe_base;

CREATE ROLE commercial;
GRANT employe_base TO commercial; -- Héritage
GRANT SELECT, INSERT, UPDATE ON commande TO commercial;

-- MySQL
CREATE ROLE 'employe_base';
GRANT SELECT ON societe.vue_clients_actifs TO 'employe_base';
CREATE ROLE 'commercial';
GRANT 'employe_base' TO 'commercial';
GRANT SELECT, INSERT, UPDATE ON societe.commande TO 'commercial';

La Vue information_schema – Le Plan des Serrures

Comment savoir qui a accès à quoi ?

-- Voir les privilèges sur une table (PostgreSQL)
SELECT grantee, privilege_type 
FROM information_schema.role_table_grants 
WHERE table_name = 'client';

-- Voir ses propres privilèges
SELECT * FROM information_schema.table_privileges 
WHERE grantee = CURRENT_USER;

🔐 Sécurité Avancée – Les Portes Dérobées et les Pièges

Vues comme Barrières de Sécurité

La stratégie la plus propre : personne n'accède directement aux tables.
Seulement aux vues. Les vues filtrent les données.

-- Table réelle (inaccessible aux utilisateurs normaux)
CREATE TABLE salaries (
    employe_id INT PRIMARY KEY,
    salaire DECIMAL(10,2),
    prime DECIMAL(10,2),
    notes_evaluation TEXT
);

-- Vue pour l'employé (ne voit que son propre salaire)
CREATE VIEW vue_mon_salaire AS
SELECT employe_id, salaire, prime
FROM salaries
WHERE employe_id = CURRENT_USER_ID(); -- Fonction magique

-- Vue pour le manager (voit son équipe)
CREATE VIEW vue_salaires_equipe AS
SELECT s.*
FROM salaries s
JOIN employe e ON s.employe_id = e.employe_id
WHERE e.manager_id = CURRENT_USER_ID();

-- Grant
GRANT SELECT ON vue_mon_salaire TO PUBLIC;
GRANT SELECT ON vue_salaires_equipe TO ROLE manager;
REVOKE ALL ON salaries FROM PUBLIC; -- Personne ne touche à la table mère

SECURITY DEFINER vs SECURITY INVOKER – Le Masque

Pour les fonctions et les vues (selon le SGBD).

  • SECURITY INVOKER (défaut) : La vue/fonction s'exécute avec les droits de celui qui l'appelle.
  • SECURITY DEFINER : La vue/fonction s'exécute avec les droits du créateur.
    Comme un passe-partout temporaire.
-- PostgreSQL : fonction avec droits élevés mais accessible aux utilisateurs
CREATE FUNCTION creer_compte_client(nom TEXT, email TEXT) 
RETURNS INTEGER
SECURITY DEFINER -- S'exécute avec MES droits
AS $$
BEGIN
    INSERT INTO client (nom, email) VALUES (nom, email);
    RETURN LASTVAL();
END;
$$ LANGUAGE plpgsql;

-- Maintenant un utilisateur basique peut appeler cette fonction
GRANT EXECUTE ON FUNCTION creer_compte_client TO redacteur_ventes;
-- Il peut insérer dans client sans avoir le droit direct sur la table.

RLS (Row Level Security) – La Sécurité par Cellule

PostgreSQL seulement (pour l'instant).
Une politique qui filtre les lignes automatiquement, quelle que soit la requête.

-- Activer RLS sur la table
ALTER TABLE commande ENABLE ROW LEVEL SECURITY;

-- Politique : un commercial ne voit que SES commandes
CREATE POLICY politique_commercial ON commande
FOR ALL -- SELECT, INSERT, UPDATE, DELETE
TO commercial
USING (commercial_id = CURRENT_USER_ID()); -- La magie

-- Politique : les managers voient tout
CREATE POLICY politique_manager ON commande
FOR ALL
TO manager
USING (true); -- Pas de filtre

-- Maintenant, même un SELECT * FROM commande filtrera automatiquement.

🎭 Cas Pratiques – Scènes de Sécurité

Cas 1 : L'Application Web

Utilisateurs : anon, client, admin
-- Vue publique (produits en stock)
CREATE VIEW vue_catalogue_public AS
SELECT id, nom, description, prix
FROM produit
WHERE en_stock = true AND actif = true;

-- Vue client (avec ses infos)
CREATE VIEW vue_mes_commandes AS
SELECT c.*, cl.nom
FROM commande c
JOIN client cl ON c.client_id = cl.client_id
WHERE cl.email = CURRENT_USER_EMAIL(); -- Fonction de l'application

-- Grants
GRANT SELECT ON vue_catalogue_public TO PUBLIC;
GRANT SELECT ON vue_mes_commandes TO ROLE client_web;
GRANT ALL ON vue_mes_commandes TO ROLE admin;
REVOKE ALL ON produit, commande, client FROM PUBLIC; -- Tables cachées

Cas 2 : Le Data Warehouse (Accès Restreint)

-- Vue "nettoyée" pour les analystes
CREATE VIEW vue_ventes_analytics AS
SELECT 
    c.commande_id,
    c.date_commande,
    c.montant_total,
    cl.region,
    EXTRACT(YEAR FROM c.date_commande) as annee,
    EXTRACT(MONTH FROM c.date_commande) as mois
FROM commande c
JOIN client cl ON c.client_id = cl.client_id
WHERE c.montant_total > 0 
  AND c.statut = 'terminee';

-- Masquer les clients spécifiques (RGPD)
CREATE VIEW vue_clients_anonymises AS
SELECT 
    client_id,
    CONCAT('Client_', client_id) as identifiant_anonyme,
    region,
    date_inscription
FROM client;

-- Grants stricts
GRANT SELECT ON vue_ventes_analytics TO ROLE analyste;
GRANT SELECT ON vue_clients_anonymises TO ROLE analyste;
-- Pas d'accès aux tables brutes. Jamais.

Cas 3 : Le Système Multi-Tenant

-- Toutes les données dans une table, séparées par tenant_id
CREATE TABLE donnees_tenants (
    tenant_id INTEGER NOT NULL,
    donnee_id SERIAL,
    contenu TEXT
);

-- Vue automatiquement filtrée par tenant
CREATE VIEW vue_mes_donnees AS
SELECT * FROM donnees_tenants 
WHERE tenant_id = CURRENT_TENANT_ID(); -- Variable de session

-- RLS encore mieux
ALTER TABLE donnees_tenants ENABLE ROW LEVEL SECURITY;
CREATE POLICY politique_tenant ON donnees_tenants
FOR ALL
TO PUBLIC
USING (tenant_id = CURRENT_SETTING('app.current_tenant')::INT);
-- Chaque connexion définit son tenant_id dans la session.

⚠️ Pièges de Sécurité Classiques – Les Portes Ouvertes

  1. PUBLIC trop permissifGRANT SELECT ON table TO PUBLIC; donne accès à tout le monde, même les nouveaux utilisateurs.
  2. Héritage de schéma public – Dans PostgreSQL, par défaut, PUBLIC a des droits sur le schéma public. À révoquer en production.
  3. Vues sans WITH CHECK OPTION – Permettent d'insérer des données "invisibles", créant des incohérences.
  4. Mots de passe par défautpostgres/postgres, root/''. Changer immédiatement.
  5. Accès depuis n'importe où'%' dans MySQL ('user'@'%'). Restreindre aux IPs de l'application.
  6. Droits d'administration trop larges – Donner ALL PRIVILEGES au compte de l'application. Créer un rôle spécifique.

Check-list de Sécurité de Base

-- PostgreSQL post-install
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON DATABASE ma_base FROM PUBLIC;

-- Créer un rôle d'application avec droits restreints
CREATE ROLE mon_app LOGIN PASSWORD 'complexe';
GRANT CONNECT ON DATABASE ma_base TO mon_app;
GRANT USAGE ON SCHEMA public TO mon_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON TOUTES_LES_TABLES_NEEDED TO mon_app;
-- Jamais ALL, jamais TRUNCATE, jamais DROP.

📜 Script de Déploiement Sécurisé

-- deploy_security.sql
BEGIN;

-- 1. Créer les rôles
CREATE ROLE lecteur;
CREATE ROLE redacteur;
CREATE ROLE admin_app;

-- 2. Créer les vues
CREATE VIEW vue_analytics AS ...;
CREATE VIEW vue_mes_donnees AS ...;

-- 3. Donner les droits sur les VUES, pas les tables
GRANT SELECT ON vue_analytics TO lecteur;
GRANT SELECT, INSERT, UPDATE ON vue_mes_donnees TO redacteur;
GRANT ALL ON vue_analytics, vue_mes_donnees TO admin_app;

-- 4. Retirer les droits sur les tables
REVOKE ALL ON client, commande, produit FROM PUBLIC;
REVOKE ALL ON client, commande, produit FROM lecteur, redacteur;

-- 5. Sécuriser le schéma
REVOKE CREATE ON SCHEMA public FROM PUBLIC;

-- 6. Configurer RLS si nécessaire (PostgreSQL)
ALTER TABLE donnees_sensibles ENABLE ROW LEVEL SECURITY;
CREATE POLICY politique_restrictive ON donnees_sensibles ...;

COMMIT;

Conclusion – La Ville en Couches

Les vues sont les façades.
La sécurité est le système de clés.
Ensemble, ils créent une ville où chacun ne voit que ce qu'il doit voir.

Souvenez-vous :

  1. Principe du moindre privilège – Donner seulement ce qui est nécessaire.
  2. Défense en profondeur – Vues + RLS + droits fins.
  3. Auditer régulièrement – Qui a accès à quoi ?
  4. Ne jamais utiliser le super-utilisateur pour l'application.

La base de données n'est pas une salle ouverte.
C'est un bâtiment avec des étages, des portes, des salles sécurisées.
Certaines portes sont des miroirs sans tain.
On regarde, sans être vu.

Prochain arrêt : les procédures stockées et les déclencheurs.
L'automatisation.
Quand la ville commence à réagir toute seule.