# Masquage dynamique

> Transcription française complète du chapitre « Masquage dynamique » du manuel DEV42, révision 26.09, pages PDF 505 à 520.

---

Index LLMS : [llms.txt](/fr/llms.txt)

---

<!-- Source: https://public.dalibo.com/exports/formation/manuels/formations/dev42/dev42.handout.html, révision 26.09. -->

# Masquage dynamique {#dev42-ch13}

## Principe du masquage dynamique {#dev42-ch13-1}

- Masquer les données personnelles à certains utilisateurs
  - mais pas tous

Avec le masquage dynamique, le propriétaire de la base peut masquer les données personnelles à certains utilisateurs, tout en laissant aux autres les droits de lire et modifier les données réelles.

## L’histoire {#dev42-ch13-2}

Paul a 2 employés :

- **Jack** s’occupe du nouveau logiciel de ventes.
  - il a besoin d’accéder aux vraies données
  - pour le RGPD c’est un « processeur de données »
- **Pierre** est un analyste qui exécute des requêtes statistiques
  - il ne doit pas avoir accès aux données personnelles

## Comment ça marche {#dev42-ch13-3}

![Illustration du manuel DEV42](/images/dev42/html/ch13-49b29c0fb5a9.png)

## Objectifs de la section {#dev42-ch13-4}

Nous allons voir :

- comment écrire des règles de masquage simple
- les avantages et limitations du masquage dynamique
- le concept de « recoupement » d’une personne (*linkability*)

## Table « company » {#dev42-ch13-5}

``` sql
DROP TABLE IF EXISTS supplier CASCADE;
DROP TABLE IF EXISTS company CASCADE;

CREATE TABLE company (
    id SERIAL PRIMARY KEY,
    name TEXT,
    vat_id TEXT UNIQUE
);
```

### Quelques données {#dev42-ch13-5-1}

``` sql
INSERT INTO company
VALUES
(952,'Shadrach', 'FR62684255667'),
(194,E'Johnny\'s Shoe Store','CHE670945644'),
(346,'Capitol Records','GB663829617823')
;
```

``` sql
SELECT * FROM company ;
```

``` default
 id  |        name         |     vat_id
-----+---------------------+----------------
 952 | Shadrach            | FR62684255667
 194 | Johnny's Shoe Store | CHE670945644
 346 | Capitol Records     | GB663829617823
```

## Table « supplier » {#dev42-ch13-6}

``` sql
CREATE TABLE supplier (
    id SERIAL PRIMARY KEY,
    fk_company_id INT REFERENCES company(id),
    contact TEXT,
    phone TEXT,
    job_title TEXT
) ;
```

### Quelques données {#dev42-ch13-6-1}

``` sql
INSERT INTO supplier
VALUES
(299,194,'Johnny Ryall','597-500-569','CEO'),
(157,346,'George Clinton', '131-002-530','Sales manager')
;
```

``` sql
SELECT * FROM supplier;
```

``` default
 id  | fk_company_id |    contact     |    phone    |   job_title
-----+---------------+----------------+-------------+---------------
 299 |           194 | Johnny Ryall   | 597-500-569 | CEO
 157 |           346 | George Clinton | 131-002-530 | Sales manager
```

## Activer l’extension {#dev42-ch13-7}

``` sql
CREATE EXTENSION IF NOT EXISTS anon CASCADE ;
SELECT anon.init() ;
```

## Activer le masquage dynamique {#dev42-ch13-8}

``` sql
ALTER DATABASE boutique
SET anon.transparent_dynamic_masking TO true ;
-- reconnexion nécessaire
```

## Rôle masqué {#dev42-ch13-9}

``` sql
SECURITY LABEL FOR anon ON ROLE pierre IS 'MASKED' ;

GRANT pg_read_all_data TO pierre;
```

- Attention : pas d’héritage du `SECURITY LABEL` !

Le rôle **pierre** devient « masqué », dans le sens où le super-utilisateur va pouvoir lui imposer un masque qui va changer sa vision des données.

En tant que Pierre, on essaie de lire la table des fournisseurs :

``` sql
\c boutique pierre
SELECT * FROM supplier;
```

``` default
 id  | fk_company_id |    contact     |    phone    |   job_title
-----+---------------+----------------+-------------+---------------
 299 |           194 | Johnny Ryall   | 597-500-569 | CEO
 157 |           346 | George Clinton | 131-002-530 | Sales manager
```

Pour le moment, il n’y a pas de règle de masquage : Pierre peut voir les données originales dans chaque table.

> PostgreSQL ne gère pas l’héritage des `SECURITY LABEL`. Il n’est donc pas possible de l’attribuer à un groupe. Il faut attribuer explicitement un `SECURITY LABEL` à **chaque rôle** concerné.

## Lister les rôles masqués {#dev42-ch13-10}

``` sql
SELECT pg_roles.rolname AS role,
       pg_shseclabel.label,
       pg_shseclabel.provider
FROM pg_shseclabel
JOIN pg_roles ON pg_shseclabel.objoid = pg_roles.oid;
```

Les rôles masqués peuvent être listés grâce à la requête ci-dessus :

``` default
  role  | label  | provider
--------+--------+----------
 pierre | MASKED | anon
```

## Masquer le nom des fournisseurs {#dev42-ch13-11}

En tant que Paul, une règle de masquage se définit ainsi :

``` sql
\c boutique paul
SECURITY LABEL FOR anon ON COLUMN supplier.contact
IS 'MASKED WITH VALUE $$CONFIDENTIAL$$';
```

Pierre essaie de lire la table des fournisseurs :

``` sql
\c boutique pierre
SELECT * FROM supplier ;
```

``` default
 id  | fk_company_id |   contact    |    phone    |   job_title
-----+---------------+--------------+-------------+---------------
 299 |           194 | CONFIDENTIAL | 597-500-569 | CEO
 157 |           346 | CONFIDENTIAL | 131-002-530 | Sales manager
```

Si Jack essaie de lire les vraies données, ce sont encore les bonnes :

``` sql
\c boutique jack
SELECT * FROM supplier;
```

``` default
 id  | fk_company_id |    contact     |    phone    |   job_title
-----+---------------+----------------+-------------+---------------
 299 |           194 | Johnny Ryall   | 597-500-569 | CEO
 157 |           346 | George Clinton | 131-002-530 | Sales manager
```

## Exercices {#dev42-ch13-12}

Pour rappel, sauf précision contraire, toutes les commandes sont à exécuter en tant que **paul** (le propriétaire).

### E201 - Deviner qui est le PDG de « Johnny’s Shoe Store » {#dev42-ch13-12-1}

Masquer le nom du fournisseur n’est pas suffisant pour anonymiser les données.

- Se connecter en tant que Pierre. Écrire une requête avec une jointure entre
- `supplier` et `company`, permettant ainsi de recouper certains fournisseurs
- en se basant sur leur poste et leur société.

Les noms des sociétés et les postes de travail sont disponibles dans de nombreux jeux de données publics. Une simple recherche sur LinkedIn ou Google révèle les noms des principaux dirigeants de la plupart des sociétés…

> On nomme « recoupement » la possibilité de rapprocher plusieurs données concernant la même personne.

### E202 - Anonymiser les sociétés {#dev42-ch13-12-2}

Nous devons donc anonymiser aussi la table `company`. Même si elle ne contient pas d’informations personnelles, certains champs peuvent être utilisés pour identifier certains de leurs employés…

- Écrire deux règles de masquage pour la table `company`.\\
- La première doit remplacer le champ `nom` avec un faux nom.\\
- La seconde remplacer `vat_id` avec une suite aléatoire de dix caractères. \\
- NB : dans la [documentation](https://postgresql-anonymizer.readthedocs.io/en/stable/), consulter les [générateurs de données factices](https://postgresql-anonymizer.readthedocs.io/en/stable/masking_functions#faking) et [fonctions aléatoires](https://postgresql-anonymizer.readthedocs.io/en/stable/masking_functions#randomization) (*faking functions*).

<!-- -->

- Vérifier que Pierre ne peut pas voir les vraies données sur la société.

### E203 - Pseudonymiser le nom des sociétés {#dev42-ch13-12-3}

À cause du masquage dynamique, les valeurs artificielles sont différentes **à chaque fois que Pierre lit la table**. Ce n’est pas toujours très pratique.

Pierre préfère appliquer tout le temps les mêmes valeurs artificielles pour une même société. Cela correspond à la « pseudonymisation ».

> La **pseudonymisation** consiste à générer systématiquement les mêmes données artificielles pour un individu donné à la place de ses données réelles.

- Écrire une nouvelle règle de masquage à partir du champ `name`,
- grâce à une [fonction de pseudonymisation](https://postgresql-anonymizer.readthedocs.io/en/stable/masking_functions#pseudonymization).

## Solutions {#dev42-ch13-13}

### S201 {#dev42-ch13-13-1}

- Se connecter en tant que Pierre. Écrire une requête avec une jointure entre
- `supplier` et `company`, permettant ainsi de recouper certains fournisseurs
- en se basant sur leur poste et leur société.

``` sql
\c boutique pierre
SELECT s.id, s.contact, s.job_title, c.name
FROM supplier s
JOIN company c ON s.fk_company_id = c.id ;
```

``` default
 id  |   contact    |   job_title   |        name
-----+--------------+---------------+---------------------
 299 | CONFIDENTIAL | CEO           | Johnny's Shoe Store
 157 | CONFIDENTIAL | Sales manager | Capitol Records
```

### S202 {#dev42-ch13-13-2}

- Écrire deux règles de masquage pour la table `company`.\\
- La première doit remplacer le champ `nom` avec un faux nom.\\
- La seconde remplacer `vat_id` avec une suite aléatoire de dix caractères. \\

``` sql
\c boutique paul

SECURITY LABEL FOR anon ON COLUMN company.name
IS 'MASKED WITH FUNCTION anon.fake_company()';

SECURITY LABEL FOR anon ON COLUMN company.vat_id
IS 'MASKED WITH FUNCTION anon.random_string(10)';
```

En tant que Paul, les données sont inchangées :

``` sql
\c boutique paul

SELECT * FROM company;
```

``` default
 id  |        name         |     vat_id
-----+---------------------+----------------
 952 | Shadrach            | FR62684255667
 194 | Johnny's Shoe Store | CHE670945644
 346 | Capitol Records     | GB663829617823
```

En tant que **pierre**, relire la table :

``` sql
\c boutique pierre

SELECT * FROM company;
```

``` default
 id  |            name             |   vat_id
-----+-----------------------------+------------
 952 | Graham, Davis and Bauer     | LYFVSI3WT5
 194 | Martinez-Smith              | 9N62K8M6JD
 346 | James, Rodriguez and Nelson | OHB20OZ4Q3
```

À chaque lecture de la table, Pierre voit des données différentes :

``` sql
SELECT * FROM company;
```

``` default
 id  |            name             |   vat_id
-----+-----------------------------+------------
 952 | Holt, Moreno and Richardson | KPAJP2Q4PK
 194 | Castillo Group              | NVGHZ1K50Z
 346 | Mccarthy-Davis              | GS3AHXBQTK
```

### S203 {#dev42-ch13-13-3}

- Écrire une nouvelle règle de masquage à partir du champ `name`,
- grâce à une fonction de pseudonymisation.

``` sql
\c boutique paul

SECURITY LABEL FOR anon ON COLUMN company.name
IS 'MASKED WITH FUNCTION anon.pseudo_company(id)';
```

Pour Pierre, les valeurs pseudonymisées resteront identiques entre deux appels (mais pas le code TVA) :

``` sql
\c boutique pierre

SELECT * FROM company;
```

``` default
 id  |      name       |   vat_id
-----+-----------------+------------
 952 | Wilkinson LLC   | IKL88GJVT4
 194 | Johnson PLC     | VOOJ6UKR6H
 346 | Young-Carpenter | DUR78F15VD
```

``` sql
SELECT * FROM company;
```

``` default
 id  |      name       |   vat_id
-----+-----------------+------------
 952 | Wilkinson LLC   | DIUAMTI653
 194 | Johnson PLC     | UND2DQGL4S
 346 | Young-Carpenter | X6EOT023AK
```
## Navigation

Pour revenir au chapitre précédent, voir [le chapitre précédent](/fr/dev42/12-masquage-statique/#dev42-ch12).
Pour poursuivre la lecture, voir [le chapitre suivant](/fr/dev42/14-sauvegardes-anonymes/#dev42-ch14).
