

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Utilisation du masquage dynamique avec Aurora PostgreSQL
<a name="AuroraPostgreSQL.Security.DynamicMasking"></a>

Le masquage dynamique des données est une fonctionnalité de sécurité qui protège les données sensibles des bases de données Aurora PostgreSQL en contrôlant la façon dont les données apparaissent aux utilisateurs au moment de la requête. Aurora l'implémente via l'`pg_columnmask`extension. `pg_columnmask`fournit une protection des données au niveau des colonnes qui complète les mécanismes natifs de sécurité au niveau des lignes et de contrôle d'accès granulaire de PostgreSQL.

Avec`pg_columnmask`, vous créez des politiques de masquage qui déterminent la visibilité des données en fonction des rôles des utilisateurs. Lorsque les utilisateurs interrogent des tables avec des politiques de masquage, Aurora PostgreSQL applique la fonction de masquage appropriée au moment de la requête en fonction du rôle de l'utilisateur et du poids des politiques. Les données sous-jacentes restent inchangées pendant le stockage.

`pg_columnmask`prend en charge les fonctionnalités suivantes :
+ Fonctions de **masquage intégrées et personnalisées : utilisez des fonctions** prédéfinies pour les modèles courants tels que le masquage des e-mails et des textes, ou créez vos propres fonctions personnalisées pour protéger les données sensibles (PII) grâce à des politiques de masquage basées sur SQL.
+ **Stratégies de masquage multiples** : masquez complètement les informations, remplacez les valeurs partielles par des caractères génériques ou définissez des approches de masquage personnalisées.
+ **Priorisation des politiques** : définissez plusieurs politiques pour une seule colonne. Utilisez des pondérations pour déterminer quelle politique de masquage doit être utilisée lorsque plusieurs politiques s'appliquent à une colonne. Aurora PostgreSQL applique des politiques basées sur le poids et l'appartenance aux rôles des utilisateurs. 

`pg_columnmask`est disponible sur Aurora PostgreSQL version 16.10 et versions ultérieures, et sur les versions 17.6 et supérieures. Il est disponible sans frais supplémentaires.

# Commencer à utiliser le masquage dynamique
<a name="AuroraPostgreSQL.Security.DynamicMasking.GetStarted"></a>

Pour masquer les données de manière dynamique, vous devez installer l'`pg_columnmask`extension dans votre base de données et créer des politiques de masquage pour vos tables. Le processus de configuration implique la vérification des conditions préalables, l'installation de l'extension, la configuration des rôles, la création de politiques et les tests de validation.

## Installation et configuration des extensions
<a name="AuroraPostgreSQL.Security.DynamicMasking.GetStarted.Installation"></a>

Connectez-vous à votre cluster Aurora PostgreSQL à l'aide de l'éditeur de requêtes de la console RDS ou d'un client PostgreSQL tel que psql avec les informations d'identification rds\$1superuser (utilisateur principal).

Exécutez la commande de création d'extension pour activer `pg_columnmask` les fonctionnalités :

```
CREATE EXTENSION pg_columnmask;
```

Cette commande installe l'`pg_columnmask`extension, crée les tables de catalogue nécessaires et enregistre les fonctions de masquage intégrées. L'installation de l'extension est spécifique à la base de données, ce qui signifie que vous devez l'installer séparément dans chaque base de données où la fonctionnalité est requise.

**Note**  
Les connexions effectuées avant l'installation de cette extension afficheront toujours des données non masquées. Fermez et reconnectez-vous pour résoudre ce problème.

Vérifiez l'installation de l'extension en vérifiant les fonctions de masquage disponibles :

```
SELECT proname FROM pg_proc
    WHERE pronamespace = 'pgcolumnmask'::regnamespace AND proname LIKE 'mask_%';
    proname     
--------Output --------
 mask_email
 mask_text
 mask_timestamp
(3 rows)
```

# Procédures de gestion des politiques de masquage des données
<a name="AuroraPostgreSQL.Security.DynamicMasking.Procedures"></a>

Vous pouvez gérer les politiques de masquage à l'aide des procédures fournies par l'`pg_columnmask`extension. Pour créer, modifier ou supprimer des politiques de masquage, vous devez disposer de l'un des privilèges suivants :
+ Propriétaire de la table sur laquelle vous créez la `pg_columnmask` politique.
+ Membre de`rds_superuser`.
+ Membre du rôle de responsable des `pg_columnmask` politiques défini par le `pgcolumnmask.policy_admin_rolname` paramètre.

La commande suivante crée une table qui sera utilisée dans les sections suivantes :

```
CREATE TABLE public.customers (
    id SERIAL PRIMARY KEY,
    name TEXT,
    phone TEXT,
    address TEXT,
    email TEXT
);
```

## CRÉER UNE POLITIQUE DE MASQUAGE
<a name="AuroraPostgreSQL.Security.DynamicMasking.Procedures.CreateMaskingPolicy"></a>

La procédure suivante crée une nouvelle politique de masquage pour une table utilisateur :

**Syntaxe**

```
create_masking_policy(
    policy_name,
    table_name,
    masking_expressions,
    roles,
    weight)
```

**Arguments**


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nom de la politique de masquage. Doit être unique par table.  | 
| table\$1name | REGCLASS |  Le qualified/unqualified nom ou l'identifiant de la table à laquelle appliquer la politique de masquage.  | 
| masking\$1expressions | JSONB |  Objet JSON contenant le nom de colonne et les paires de fonctions de masquage. Chaque clé est un nom de colonne et sa valeur est l'expression de masquage à appliquer à cette colonne.  | 
| roles | NOM [] |  Les rôles auxquels s'applique cette politique de masquage. La valeur par défaut est PUBLIC.  | 
| weight | INT |  Le poids de la politique de masquage. Lorsque plusieurs politiques sont applicables à la requête d'un utilisateur donné, la politique ayant le poids le plus élevé (nombre entier le plus élevé) sera appliquée à chaque colonne masquée. La valeur par défaut est 0. Il n'y a pas deux politiques de masquage proposées sur la table qui peuvent avoir le même poids.  | 

**Type de retour**

Aucune

**Example de la création d'une politique de masquage qui masque la colonne d'e-mail associée au `test_user` rôle :**  

```
CALL pgcolumnmask.create_masking_policy(
    'customer_mask',
    'public.customers',
    JSON_OBJECT('{
        "email", "pgcolumnmask.mask_email(email)"
    }')::JSONB,
    ARRAY['test_user'],
    100
);
```

## ALTER\$1MASKING\$1POLICY
<a name="AuroraPostgreSQL.Security.DynamicMasking.Procedures.AlterMaskingPolicy"></a>

Cette procédure modifie une politique de masquage existante. `ALTER_MASKING_POLICY`peut modifier les expressions de masquage de la politique, l'ensemble des rôles auxquels la politique s'applique et le poids de la politique de masquage. Lorsque l'un de ces paramètres est omis, la partie correspondante de la politique reste inchangée.

**Syntaxe**

```
alter_masking_policy(
    policy_name,
    table_name,
    masking_expressions,
    roles,
    weight)
```

**Arguments**


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nom existant de la politique de masquage.  | 
| table\$1name | REGCLASS |   qualified/unqualified Nom oid de la table contenant la politique de masquage.  | 
| masking\$1expressions | JSONB |  Nouvel objet JSON contenant le nom de colonne et les paires de fonctions de masquage ou NULL dans le cas contraire.  | 
| roles | NOM [] |  La liste des nouveaux rôles auxquels s'applique cette politique de masquage ou NULL dans le cas contraire.  | 
| weight | INT |  Nouveau poids pour la politique de masquage ou NULL dans le cas contraire.  | 

**Type de retour**

Aucune

**Example d'ajouter le rôle d'analyste à une politique de masquage existante sans modifier les autres attributs de la stratégie.**  

```
CALL pgcolumnmask.alter_masking_policy(
    'customer_mask',
    'public.customers',
    NULL,
    ARRAY['test_user', 'analyst'],
    NULL 
);

-- Alter the weight of the policy without altering other details
CALL pgcolumnmask.alter_masking_policy(
    'customer_mask',
    'customers',
    NULL,
    NULL,
    4
);
```

## DROP\$1MASKING\$1POLICY
<a name="AuroraPostgreSQL.Security.DynamicMasking.Procedures.DropMaskingPolicy"></a>

Cette procédure supprime une politique de masquage existante.

**Syntaxe**

```
drop_masking_policy(
        policy_name,
        table_name)
```

**Arguments**


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nom existant de la politique de masquage.  | 
| table\$1name | REGCLASS |   qualified/unqualified Nom oid de la table contenant la politique de masquage.  | 

**Type de retour**

Aucune

**Example de supprimer la politique de masquage customer\$1mask**  

```
-- Drop a masking policy
    CALL pgcolumnmask.drop_masking_policy(
        'customer_mask',
        'public.customers',
    );
```

# Identifiants échappés dans la procédure DDL de politique de masquage
<a name="AuroraPostgreSQL.Security.DynamicMasking.EscapeIdentifiers"></a>

Lors de la création de politiques de masquage de données avec des identifiants entre guillemets, un échappement approprié est nécessaire pour garantir l'exactitude des références aux objets et l'application des politiques. Pour utiliser des identifiants entre guillemets dans les procédures de gestion des politiques de `pg_columnmask` masquage :
+ **Nom de la politique** — Doit être placé entre guillemets.
+ **Nom de la table** : le nom du schéma et le nom de la table doivent être placés individuellement entre guillemets lorsque cela est nécessaire.
+ **Expressions de masquage** : les noms des colonnes et des fonctions dans les expressions de masquage doivent être placés entre guillemets et les guillemets eux-mêmes doivent être évités à l'aide d'une barre oblique inverse.
+ **Rôles** — Le tableau des noms de rôles est automatiquement cité entre guillemets. Le nom du rôle doit correspondre exactement au nom indiqué en tenant compte de la distinction `pg_roles` majuscules/minuscules.

**Example de syntaxe d'échappement et de citation**  
Cet exemple montre la syntaxe d'échappement et de citation appropriée lors de la création de politiques de masquage pour les tables, les colonnes, les fonctions et les rôles qui utilisent des noms mixtes ou nécessitent des identifiants entre guillemets dans Aurora PostgreSQL.  

```
-- Create a table and columns with mixed case name 
CREATE TABLE public."Employees" (
    "Name" TEXT,
    "Email" TEXT,
    ssn VARCHAR(20)
);

-- Create a role with mixed case name
CREATE ROLE "Masked_user";

-- Create a function with mixed case name
CREATE OR REPLACE FUNCTION public."MaskEmail"(text)
    RETURNS character varying
    LANGUAGE plpgsql
    IMMUTABLE PARALLEL SAFE
    AS $$ BEGIN
        RETURN 'XXXXXXXX'::text;
    END $$;

-- Now use these objects with mixed case names in
-- masking policy management procedures
CALL pgcolumnmask.create_masking_policy(
    '"Policy1"',  -- policy name should be surrounded with double quotes for quoting
    'public."Employees"', -- table and schema name should be individually 
                          -- surrounded with double quotes for quoting
    JSON_OBJECT('{
        "\"Email\"", "\"MaskEmail\"(\"Email\")"
    }')::JSONB, -- masking expression should have double quotes around function names
                -- and columns names etc when needed. Also the double quotes itself
                -- should be escaped using \ (backslash) since this is a JSON string
    ARRAY['Masked_user'], -- Rolename do not need quoting
                          -- (this behaviour may change in future release)
    100
);

SELECT * FROM pgcolumnmask.pg_columnmask_policies
    WHERE tablename = 'Employees';
-[ RECORD 1 ]-----+-------------------------------------
schemaname        | public
tablename         | Employees
policyname        | Policy1
roles             | {Masked_user}
masked_columns    | {Email}
masking_functions | {"(\"MaskEmail\"(\"Email\"))::text"}
weight            | 100
```

## Vues administratives
<a name="AuroraPostgreSQL.Security.DynamicMasking.AdminViews"></a>

Vous pouvez consulter l'ensemble de la `pg_columnmask` politique à l'aide de la vue `pgcolumnmask.pg_columnmask_policies` administrative accessible au public. Les informations suivantes sont disponibles via cette vue. La vue renvoie uniquement les politiques de masquage détenues par l'utilisateur actuel.


| Nom de la colonne | Type de données | Description | 
| --- | --- | --- | 
|  schemaname  | NAME |  Schéma de la relation à laquelle la politique est attachée  | 
|  tablename  | NAME |  Nom de la relation à laquelle la politique est attachée  | 
|  nom de la politique  | NAME |  Nom de la politique de masquage, toutes les politiques de masquage ont des noms uniques  | 
|  roles  | TEXTE [] |  Rôle auquel la politique s'applique.  | 
|  colonnes\$1masquées  | TEXTE [] |  Colonnes masquées  | 
|  fonctions\$1masquage  | TEXTE [] |  Fonctions de masquage  | 
| weight | INT |  Poids de la politique ci-jointe  | 

# Fonctions de masquage de données prédéfinies
<a name="AuroraPostgreSQL.Security.DynamicMasking.PredefinedMaskingFunctions"></a>

`pg_columnmask`L'extension fournit des fonctions utilitaires intégrées écrites en langage C (pour une exécution plus rapide) qui peuvent être utilisées comme expression de masquage pour les `pg_columnmask` politiques.

**texte\$1masque**

Fonction permettant de masquer les données textuelles avec des options de visibilité configurables.

**Arguments**


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| input | TEXT |  La chaîne de texte d'origine à masquer  | 
| mask\$1char | CHAISE (1) |  Caractère utilisé pour le masquage (par défaut : « X »)  | 
| visible\$1prefix | INT |  Nombre de caractères au début du texte saisi qui resteront démasqués (par défaut : 0)  | 
| visible\$1suffix | INT |  Nombre de caractères à la fin du texte saisi qui resteront démasqués (par défaut : 0)  | 
| use\$1hash\$1mask | BOOLEAN |  Si VRAI, utilise un masquage basé sur le hachage au lieu de mask\$1char (par défaut : FALSE)  | 

**Example d'utiliser différentes options de masquage**  
Masquer l'intégralité de la chaîne d'entrée avec le caractère « X » par défaut  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World');
  mask_text  
-------------
 XXXXXXXXXXX
```
Utilisez l'`mask_char`argument pour masquer la saisie de texte à l'aide d'un caractère différent  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World', '*');
  mask_text  
-------------
 ***********
```
Utilisation `visible_prefix` et `visible_suffix` paramètres pour contrôler le nombre de caractères non masqués au début et à la fin du texte  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World', '*', 5, 1);
  mask_text  
-------------
 Hello*****d
```
Lorsque `use_hash_mask` c'est vrai, la chaîne d'entrée est masquée à l'aide de caractères aléatoires, l'`mask_char`argument est ignoré mais `visible_prefix` est `visible_suffix` toujours respecté.  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World', '*', 2, 2, true);
  mask_text  
-------------
 Hex36dOHild
```

**mask\$1timestamp**


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| ts\$1to\$1mask | TIMESTAMP |  L'horodatage d'origine à masquer  | 
| mask\$1part | TEXT |  Spécifie la partie de l'horodatage à masquer (par défaut : « toutes ») Valeurs valides : « année », « mois », « jour », « heure », « minute », « seconde », « tout »  | 
| mask\$1value | TIMESTAMP |  La valeur d'horodatage à utiliser pour le masquage (par défaut : '1900-01-01 00:00:00 ')  | 

**Example d'utilisation de `mask_timestamps`**  
Ces exemples illustrent le masquage complet de l'horodatage à une valeur par défaut, le masquage partiel de composants d'horodatage spécifiques (année uniquement) et le masquage avec une valeur de remplacement personnalisée.  
Masquer complètement la valeur d'entrée à l'horodatage par défaut  

```
postgres=> SELECT pgcolumnmask.mask_timestamp('2023-06-15 14:30:00');
   mask_timestamp    
---------------------
 1900-01-01 00:00:00
```
Pour masquer une seule partie de l'horodatage (par exemple, uniquement l'année)  

```
postgres=> SELECT pgcolumnmask.mask_timestamp('2023-06-15 14:30:00', 'year');
   mask_timestamp    
---------------------
 1900-06-15 14:30:00
```
Pour modifier la valeur masquée de l'horodatage, utilisez l'argument `mask_value`  

```
postgres=> SELECT pgcolumnmask.mask_timestamp('2023-06-15 14:30:00', 'all', '2012-12-12 12:12:12');
   mask_timestamp    
---------------------
 2012-12-12 12:12:12
```

**mask\$1timestamp**

Fonction permettant de masquer les adresses e-mail tout en préservant la structure des e-mails.


| Paramètre | Datatype | Description | 
| --- | --- | --- | 
| input | TEXT |  L'adresse e-mail d'origine à masquer  | 
| mask\$1char | CHAISE (1) |  Caractère utilisé pour le masquage (par défaut : « X »)  | 
| mask\$1local | BOOLEAN |  Si VRAI, masque la partie locale de l'e-mail (avant @) (par défaut : TRUE)  | 
| mask\$1domain | BOOLEAN |  Si VRAI, masque la partie domaine de l'e-mail (après @) (par défaut : TRUE)  | 

**Example d'utilisation de `mask_email`**  
Ces exemples illustrent le masquage complet des e-mails, les caractères de masque personnalisés et le masquage sélectif de la partie locale ou de la partie domaine de l'adresse e-mail.  
Masquage complet  

```
postgres=> SELECT pgcolumnmask.mask_email('user@example.com');
    mask_email    
------------------
 XXXX@XXXXXXX.com
```
`mask_char`À utiliser pour modifier le caractère utilisé pour le masquage  

```
postgres=> SELECT pgcolumnmask.mask_email('user@example.com', '*');
    mask_email    
------------------
 ****@*******.com
```
Utiliser `mask_local` et `mask_domain` contrôler le masquage en local et sur le domaine  

```
postgres=> SELECT pgcolumnmask.mask_email('user@example.com', '*', true, false);
    mask_email    
------------------
 ****@example.com

postgres=> SELECT pgcolumnmask.mask_email('user@example.com', '*', false, true);
    mask_email    
------------------
 user@*******.com
```

# Implémentation de pg\$1columnmask dans un flux de travail end-to-end
<a name="AuroraPostgreSQL.Security.DynamicMasking.WorkflowExample"></a>

Cette section décrit une implémentation complète de `pg_columnmask` l'utilisation d'un exemple de table d'employés contenant des données sensibles. Vous apprendrez à créer des fonctions de masquage personnalisées, à définir plusieurs politiques de masquage avec différents niveaux de pondération pour différents rôles (stagiaire, support, analyste) et à observer comment les utilisateurs ayant une appartenance à un ou plusieurs rôles voient différents niveaux de données masquées. Les exemples couvrent également le comportement de masquage dans les instructions DML avec des clauses RETURNING, les déclencheurs sur les tables par rapport aux vues, et les opérations de gestion des politiques, notamment le changement de nom, la modification des poids et le nettoyage.

1. Créez un exemple de table contenant des données sensibles :

   ```
   CREATE SCHEMA hr;
   
   CREATE TABLE hr.employees (
       id INT PRIMARY KEY,
       name TEXT NOT NULL,
       email TEXT,
       ssn TEXT,
       salary NUMERIC(10,2)
    );
   
   INSERT INTO hr.employees VALUES
       (1, 'John Doe', 'john.doe@example.com', '123-45-6789', 50000.00),
       (2, 'Jane Smith', 'jane.smith@example.com', '987-65-4321', 60000.00);
   ```

1. Créez des fonctions de masquage personnalisées :

   ```
   CREATE OR REPLACE FUNCTION public.mask_ssn(ssn TEXT)
       RETURNS TEXT AS $$
       BEGIN
           RETURN 'XXX-XX-' || RIGHT(ssn, 4);
       END;
       $$ LANGUAGE plpgsql;
   
   CREATE OR REPLACE FUNCTION public.mask_salary(salary NUMERIC, multiplier NUMERIC DEFAULT 0.0)
       RETURNS NUMERIC AS $$
       BEGIN
           RETURN salary * multiplier;
       END;
       $$ LANGUAGE plpgsql;
   ```

1. Créez plusieurs politiques avec différents niveaux de masquage en fonction des rôles des utilisateurs :

   ```
   -- Create different roles
   CREATE ROLE analyst_role;
   CREATE ROLE support_role;
   CREATE ROLE intern_role;
   
   GRANT USAGE ON SCHEMA hr TO analyst_role, support_role, intern_role;
   GRANT SELECT ON hr.employees TO analyst_role, support_role, intern_role;
   ----------------------------------------------------------------------
   
   -- Low-Weight Policy (Intern)
   CALL pgcolumnmask.create_masking_policy(
       'employee_mask_strict',
       'hr.employees',
       JSON_BUILD_OBJECT('name', 'pgcolumnmask.mask_text(name, ''*'')',
                         'email', 'pgcolumnmask.mask_email(email)',
                         'ssn', 'pgcolumnmask.mask_text(ssn, ''*'')',
                         'salary', 'public.mask_salary(salary)')::JSONB,
       ARRAY['intern_role'],
       10  -- Lowest weight
   );
   
   ----------------------------------------------------------------------
   -- Medium-Weight Policy (Support)
   CALL pgcolumnmask.create_masking_policy(
       'employee_mask_moderate',
       'hr.employees',
       JSON_BUILD_OBJECT('email', 'pgcolumnmask.mask_email(email, ''#'')',
                         'ssn', 'public.mask_ssn(ssn)',
                         'salary', 'public.mask_salary(salary)')::JSONB,
       ARRAY['support_role'],
       50   -- Medium weight
   );
   
   ----------------------------------------------------------------------
   -- High-Weight Policy (Analyst)
   CALL pgcolumnmask.create_masking_policy(
       'employee_mask_light',
       'hr.employees',
       JSON_BUILD_OBJECT('ssn', 'public.mask_ssn(ssn)',
                         'salary', 'public.mask_salary(salary, 0.9)')::JSONB,
       ARRAY['analyst_role'],
       100   -- Highest weight
   );
   ```

1. Les exemples suivants montrent comment les différents utilisateurs voient les données en fonction de leur appartenance aux rôles et de leur pondération des politiques.

   ```
   -- Create users
   CREATE USER sarah_intern;
   GRANT intern_role TO sarah_intern;
   
   CREATE USER lisa_support;
   GRANT support_role TO lisa_support;
   
   CREATE USER mike_analyst;
   GRANT analyst_role TO mike_analyst;
   
   CREATE USER ethan_support_intern;
   GRANT support_role, intern_role TO ethan_support_intern;
   
   CREATE USER john_analyst_intern;
   GRANT analyst_role, intern_role TO john_analyst_intern;
   ```

   En tant que stagiaire (masquage le plus strict) :

   ```
   SET ROLE sarah_intern;
   
   SELECT * FROM hr.employees;
    id |    name    |         email          |     ssn     | salary 
   ----+------------+------------------------+-------------+--------
     1 | ********   | XXXXXXXX@XXXXXXX.com   | *********** |   0.00
     2 | ********** | XXXXXXXXXX@XXXXXXX.com | *********** |   0.00
   ```

   En tant qu'utilisateur du support (masquage modéré) :

   ```
   SET ROLE lisa_support;
   
   SELECT * FROM hr.employees;
    id |    name    |         email          |     ssn     | salary 
   ----+------------+------------------------+-------------+--------
     1 | John Doe   | ########@#######.com   | XXX-XX-6789 |   0.00
     2 | Jane Smith | ##########@#######.com | XXX-XX-4321 |   0.00
   ```

   En tant qu'analyste (masquage le plus léger) :

   ```
   SET ROLE mike_analyst;
   
   SELECT * FROM hr.employees;
    id |    name    |         email          |     ssn     |  salary  
   ----+------------+------------------------+-------------+----------
     1 | John Doe   | john.doe@example.com   | XXX-XX-6789 | 45000.00
     2 | Jane Smith | jane.smith@example.com | XXX-XX-4321 | 54000.00
   ```

   En tant qu'utilisateur ethan\$1support\$1intern qui est à la fois stagiaire et utilisateur de support :

   ```
   SET ROLE ethan_support_intern;
   
   -- masking policies appliable to this user: employee_mask_strict and employee_mask_moderate
   -- id : unmasked because no masking policy appliable on ethan_support_intern
   --            masks these columns
   -- name : masked because of employee_mask_strict policy
   -- email, ssn, salary : both employee_mask_strict and employee_mask_moderate mask these columns
   --                      but employee_mask_moderate will be use because of higher weight 
   
   SELECT * FROM hr.employees;
    id |    name    |         email          |     ssn     | salary 
   ----+------------+------------------------+-------------+--------
     1 | ********   | ########@#######.com   | XXX-XX-6789 |   0.00
     2 | ********** | ##########@#######.com | XXX-XX-4321 |   0.00
   ```

   En tant que john\$1analyst\$1intern qui est à la fois stagiaire et analyste :

   ```
   SET ROLE john_analyst_intern;
   
   -- masking policies appliable to this user: employee_mask_strict and employee_mask_light
   -- id : unmasked because no masking policy appliable on john_analyst_intern
   --            masks these columns
   -- name, email : masked because of employee_mask_strict
   -- ssn, salary : both employee_mask_strict and employee_mask_light mask these columns
   --               but employee_mask_light will be use because of higher weight 
   
   SELECT * FROM hr.employees;
    id |    name    |         email          |     ssn     |  salary  
   ----+------------+------------------------+-------------+----------
     1 | ********   | XXXXXXXX@XXXXXXX.com   | XXX-XX-6789 | 45000.00
     2 | ********** | XXXXXXXXXX@XXXXXXX.com | XXX-XX-4321 | 54000.00
   ```

# Comprendre le comportement de masquage dans les opérations DML
<a name="AuroraPostgreSQL.Security.DynamicMasking.DMLMasking"></a>

`pg_columnmask`s'applique de manière cohérente à toutes les opérations DML, y compris les instructions INSERT, UPDATE, DELETE et MERGE. Lorsque vous exécutez ces opérations, Aurora PostgreSQL masque les données selon un principe fondamental : toutes les données lues depuis le stockage sont masquées conformément aux politiques applicables de l'utilisateur actuel.

Le masquage affecte certains des composants de requête suivants, tels que :
+ clauses WHERE
+ Conditions d'adhésion
+ Sous-requêtes
+ Clauses de retour

Tous ces composants fonctionnent sur des valeurs masquées, et non sur les données d'origine. Lorsque les données sont enregistrées dans le stockage sans être masquées, les utilisateurs ne voient leur vue masquée que lorsqu'ils les relisent.

Aurora PostgreSQL applique toutes les contraintes de base de données (NOT NULL, UNIQUE, CHECK, FOREIGN KEY) aux valeurs stockées réelles, et non aux valeurs masquées. Cela peut parfois créer des incohérences apparentes si les fonctions de masquage ne sont pas conçues avec soin.

Le masquage fonctionne parallèlement aux autorisations au niveau des colonnes :
+ Les utilisateurs sans privilèges SELECT ne peuvent pas lire les colonnes
+ Les utilisateurs dotés de privilèges SELECT voient les valeurs masquées conformément à leurs politiques applicables

# Comprendre le comportement de masquage dans les fonctions de déclenchement
<a name="AuroraPostgreSQL.Security.DynamicMasking.TriggerFunctionMasking"></a>

Lorsque `pg_columnmask` des politiques sont appliquées à des tables, il est important de comprendre comment le masquage interagit avec les fonctions de déclenchement. Les déclencheurs sont des fonctions de base de données qui s'exécutent automatiquement en réponse à certains événements sur une table, tels que les opérations INSERT, UPDATE ou DELETE.

Par défaut, DDM applique différentes règles de masquage en fonction du type de déclencheur :

déclencheurs de table  
**Les tables de transition sont démasquées** : les fonctions de déclenchement des tables ont accès aux données non masquées de leurs tables de transition, tant pour les anciennes que pour les nouvelles versions de lignes  
Les propriétaires de tables créent des déclencheurs et sont propriétaires des données, de sorte qu'ils disposent d'un accès complet pour gérer efficacement leurs tables

Afficher les déclencheurs (AU LIEU DES déclencheurs)  
**Les tables de transition sont masquées** : les fonctions de déclenchement des vues voient les données masquées en fonction des autorisations de l'utilisateur actuel  
Les propriétaires des vues peuvent être différents des propriétaires des tables de base et doivent respecter les politiques de masquage des tables sous-jacentes

Deux paramètres de configuration au niveau du serveur contrôlent le comportement des déclencheurs avec des tables masquées. Ils ne peuvent être définis que par `rds_superuser` :
+ **Restreindre les déclencheurs sur les tables masquées** : empêche l'exécution de déclencheurs lorsqu'un utilisateur masqué effectue des opérations DML sur des tables avec des politiques de masquage applicables.
+ **Restreindre les déclencheurs sur les vues contenant des tables masquées :** — Empêche l'exécution des déclencheurs sur les vues lorsque la définition de la vue inclut des tables avec des politiques de masquage applicables à l'utilisateur actuel.

**Example des différences entre l'application de la fonction à la table et à la vue**  
L'exemple suivant crée une fonction de déclenchement qui imprime les anciennes et les nouvelles valeurs de ligne, puis montre comment la même fonction se comporte différemment lorsqu'elle est attachée à une table par rapport à une vue.  

```
-- Create trigger function
CREATE OR REPLACE FUNCTION print_changes()
    RETURNS TRIGGER AS
    $$
        BEGIN
        RAISE NOTICE 'Old row: name=%, email=%, ssn=%, salary=%',
            OLD.name, OLD.email, OLD.ssn, OLD.salary;
        
        RAISE NOTICE 'New row: name=%, email=%, ssn=%, salary=%',
            NEW.name, NEW.email, NEW.ssn, NEW.salary;
        
        RETURN NEW;
        END;
    $$ LANGUAGE plpgsql;

-- Create trigger
CREATE TRIGGER print_changes_trigger
    BEFORE UPDATE ON hr.employees
    FOR EACH ROW
    EXECUTE FUNCTION print_changes();

-- Grant update to analyst role
GRANT UPDATE ON hr.employees TO analyst_role;

-- Unmasked data must be seen inside trigger even for masked user for the OLD and NEW
-- row passed to trigger function
BEGIN;
SET ROLE mike_analyst;
UPDATE hr.employees SET id = id + 10 RETURNING *;
NOTICE:  Old row: name=John Doe, email=john.doe@example.com, ssn=123-45-6789, salary=50000.00
NOTICE:  New row: name=John Doe, email=john.doe@example.com, ssn=123-45-6789, salary=50000.00
NOTICE:  Old row: name=Jane Smith, email=jane.smith@example.com, ssn=987-65-4321, salary=60000.00
NOTICE:  New row: name=Jane Smith, email=jane.smith@example.com, ssn=987-65-4321, salary=60000.00
 id |    name    |         email          |     ssn     |  salary  
----+------------+------------------------+-------------+----------
 11 | John Doe   | john.doe@example.com   | XXX-XX-6789 | 45000.00
 12 | Jane Smith | jane.smith@example.com | XXX-XX-4321 | 54000.00
(2 rows)

ROLLBACK;


-- Triggers on views (which are supposed to see masked data for new/old row)
CREATE VIEW hr.view_over_employees AS SELECT * FROM hr.employees;
GRANT UPDATE, SELECT ON hr.view_over_employees TO analyst_role;

-- Create trigger for this view
CREATE TRIGGER print_changes_trigger
    INSTEAD OF UPDATE ON hr.view_over_employees
    FOR EACH ROW
    EXECUTE FUNCTION print_changes();

-- Masked new and old rows should be passed to trigger if trigger is on view
BEGIN;
SET ROLE mike_analyst;
UPDATE hr.view_over_employees SET id = id + 10 RETURNING *;
NOTICE:  Old row: name=John Doe, email=john.doe@example.com, ssn=XXX-XX-6789, salary=45000.00
NOTICE:  New row: name=John Doe, email=john.doe@example.com, ssn=XXX-XX-6789, salary=45000.00
NOTICE:  Old row: name=Jane Smith, email=jane.smith@example.com, ssn=XXX-XX-4321, salary=54000.00
NOTICE:  New row: name=Jane Smith, email=jane.smith@example.com, ssn=XXX-XX-4321, salary=54000.00
 id |    name    |         email          |     ssn     |  salary  
----+------------+------------------------+-------------+----------
 11 | John Doe   | john.doe@example.com   | XXX-XX-6789 | 45000.00
 12 | Jane Smith | jane.smith@example.com | XXX-XX-4321 | 54000.00
(2 rows)
ROLLBACK;
```
Nous vous recommandons de revoir le comportement des déclencheurs avant de les implémenter sur des tables masquées. Les déclencheurs de table ont accès aux données non masquées des tables de transition, tandis que les déclencheurs de vue voient les données masquées.

**Example de renommer la politique de masquage**  
L'exemple suivant montre comment renommer les politiques existantes à l'aide de la `rename_masking_policy` procédure.  

```
-- Rename the strict policy
CALL pgcolumnmask.rename_masking_policy(
    'employee_mask_strict',
    'hr.employees',
    'intern_protection_policy'
);

-- Verify the rename
SELECT policyname, roles, weight
    FROM pgcolumnmask.pg_columnmask_policies
    WHERE tablename = 'employees'
    ORDER BY weight DESC;

        policyname        |     roles      | weight 
--------------------------+----------------+--------
 employee_mask_light      | {analyst_role} |    100
 employee_mask_moderate   | {support_role} |     50
 intern_protection_policy | {intern_role}  |     10
```

**Example de la modification du poids des politiques**  
L'exemple suivant montre comment modifier les pondérations des politiques pour modifier leur pondération.  

```
-- Change weight of moderate policy
CALL pgcolumnmask.alter_masking_policy(
    'employee_mask_moderate'::NAME,
    'hr.employees'::REGCLASS,
    NULL,    -- Keep existing masking expressions
    NULL,    -- Keep existing roles
    75       -- New weight
);

-- Verify the changes
SELECT policyname, roles, weight
    FROM pgcolumnmask.pg_columnmask_policies
    WHERE tablename = 'employees'
    ORDER BY weight DESC;
        policyname        |     roles      | weight 
--------------------------+----------------+--------
 employee_mask_light      | {analyst_role} |    100
 employee_mask_moderate   | {support_role} |     75
 intern_protection_policy | {intern_role}  |     10
```

**Example de nettoyage**  
L'exemple suivant montre comment supprimer toutes les politiques, tables et utilisateurs.  

```
-- Drop policies
CALL pgcolumnmask.drop_masking_policy(
    'intern_protection_policy',
    'hr.employees'
);

CALL pgcolumnmask.drop_masking_policy(
    'employee_mask_moderate',
    'hr.employees'
);

CALL pgcolumnmask.drop_masking_policy(
    'employee_mask_light',
    'hr.employees'
);

-- Drop table and functions
DROP VIEW IF EXISTS hr.view_over_employees;
DROP TABLE IF EXISTS hr.employees;
DROP SCHEMA IF EXISTS hr;
DROP FUNCTION IF EXISTS public.mask_ssn(text);
DROP FUNCTION IF EXISTS public.mask_salary(numeric, numeric);

-- Drop users
DROP USER sarah_intern, lisa_support, mike_analyst,
    ethan_support_intern, john_analyst_intern;
DROP ROLE intern_role, support_role, analyst_role;
```

# Configuration du rôle de gestion des politiques de masquage
<a name="AuroraPostgreSQL.Security.DynamicMasking.PolicyManagementRole"></a>

L'extension `pg_columnmask` de masquage de colonnes PostgreSQL vous permet de déléguer la gestion des politiques de masquage à un rôle spécifique, plutôt que `rds_superuser` d'exiger ou de détenir des privilèges de propriétaire de table. Cela permet de contrôler de manière plus précise les personnes habilitées à créer, modifier et supprimer les politiques de masquage.

Pour configurer le rôle qui bénéficiera des privilèges de gestion des politiques de masquage, procédez comme suit :

1. Création du rôle d'administrateur des politiques — En tant que tel`rds_superuser`, créez un nouveau rôle chargé de gérer les politiques de masquage :

   ```
   CREATE ROLE mask_admin NOLOGIN;
   ```

1. Configurer le paramètre PostgreSQL : dans votre groupe de paramètres de cluster de base de données personnalisé, définissez `pgcolumnmask.policy_admin_rolname` le paramètre de configuration du moteur sur le nom du rôle que vous avez créé :

   ```
   pgcolumnmask.policy_admin_rolname = mask_admin
   ```

   Les paramètres de configuration de ce moteur peuvent être définis dans un groupe de paramètres de cluster de base de données et ne nécessitent pas de redémarrage de l'instance. Pour plus de détails sur la mise à jour des paramètres, consultez[Modification des paramètres d'un groupe de paramètres de cluster de base de données dans Amazon Aurora](USER_WorkingWithParamGroups.ModifyingCluster.md).

1. Accordez le rôle aux utilisateurs En général`rds_superuser`, accordez le `mask_admin` rôle aux utilisateurs qui devraient être en mesure de gérer les politiques de masquage :

   ```
   CREATE USER alice LOGIN;
   CREATE USER bob LOGIN;
   GRANT mask_admin TO alice, bob;
   ```

   En outre, assurez-vous que les utilisateurs disposent du privilège USAGE sur les schémas dans lesquels ils géreront les politiques de masquage :

   ```
   GRANT USAGE ON SCHEMA hr TO alice, bob;
   ```

Désormais, lorsque `alice` les utilisateurs `bob` se connectent à la base de données, ils peuvent utiliser les fonctions d'`pg_columnmask`extension standard pour créer, modifier et supprimer des politiques de masquage sur toutes les tables de tous les schémas pour lesquels ils ont des `USAGE` privilèges sur le schéma.

# Meilleures pratiques pour une implémentation sécurisée de pg\$1columnmask
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices"></a>

La section suivante présente les meilleures pratiques de sécurité `pg_columnmask` à mettre en œuvre dans votre environnement Aurora PostgreSQL. Suivez ces recommandations pour :
+ Mettre en place une architecture de contrôle d'accès sécurisée basée sur les rôles
+ Développez des fonctions de masquage qui préviennent les failles de sécurité
+ Comprenez et contrôlez le comportement des déclencheurs à l'aide de données masquées

## Architecture de sécurité basée sur les rôles
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.architecture"></a>

Définissez une hiérarchie des rôles pour implémenter des contrôles d'accès dans votre base de données. Aurora `pg_columnmask` PostgreSQL renforce ces contrôles en fournissant une couche supplémentaire pour un masquage précis des données au sein de ces rôles.

Créez des rôles dédiés qui correspondent aux fonctions de l'organisation plutôt que d'accorder des autorisations à des utilisateurs individuels. Cette approche améliore l'auditabilité et simplifie la gestion des autorisations au fur et à mesure de l'évolution de votre structure organisationnelle.

**Example de créer une hiérarchie des rôles organisationnels**  
L'exemple suivant crée une hiérarchie des rôles organisationnels avec des rôles dédiés à différentes fonctions, puis affecte des utilisateurs individuels aux rôles appropriés. Dans cet exemple, les rôles organisationnels (analyst\$1role, support\$1role) sont créés en premier, puis les utilisateurs individuels obtiennent l'adhésion à ces rôles. Cette structure vous permet de gérer les autorisations au niveau du rôle plutôt que pour chaque utilisateur individuel.  

```
-- Create organizational role hierarchy
CREATE ROLE data_admin_role;
CREATE ROLE security_admin_role;
CREATE ROLE analyst_role;
CREATE ROLE support_role;
CREATE ROLE developer_role;

-- Specify security_admin_role as masking policy manager in the DB cluster parameter
-- group pgcolumnmask.policy_admin_rolname = 'security_admin_role'

-- Create specific users and assign to appropriate roles
CREATE USER security_manager;
CREATE USER data_analyst1, data_analyst2;
CREATE USER support_agent1, support_agent2;

GRANT security_admin_role TO security_manager;
GRANT analyst_role TO data_analyst1, data_analyst2;
GRANT support_role TO support_agent1, support_agent2;
```
Mettez en œuvre le principe du moindre privilège en n'accordant que les autorisations minimales nécessaires pour chaque rôle. Évitez d'accorder des autorisations étendues qui pourraient être exploitées si les informations d'identification sont compromises.  

```
-- Grant specific table permissions rather than schema-wide access
GRANT SELECT ON sensitive_data.customers TO analyst_role;
GRANT SELECT ON sensitive_data.transactions TO analyst_role;
-- Do not grant: GRANT ALL ON SCHEMA sensitive_data TO analyst_role;
```
Les administrateurs de politiques ont besoin de `USAGE` privilèges sur les schémas dans lesquels ils gèrent les politiques de masquage. Accordez ces privilèges de manière sélective, conformément au principe du moindre privilège. Révisez régulièrement les autorisations d'accès aux schémas pour vous assurer que seul le personnel autorisé dispose des capacités de gestion des politiques.  
La configuration des paramètres du rôle d'administrateur des politiques est réservée aux administrateurs de base de données uniquement. Ce paramètre ne peut pas être modifié au niveau de la base de données ou de la session, ce qui empêche les utilisateurs non privilégiés de remplacer les attributions des administrateurs de politiques. Cette restriction garantit que le contrôle des politiques de masquage reste centralisé et sécurisé.  
Attribuez le rôle d'administrateur des politiques à des personnes spécifiques plutôt qu'à des groupes. Cette approche ciblée garantit un accès sélectif à la gestion des politiques de masquage, car les administrateurs de politiques ont la possibilité de masquer toutes les tables de la base de données. 

## Développement de fonctions de masquage sécurisées
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.MaskingDevelopment"></a>

Développez des fonctions de masquage à l'aide d'une sémantique de liaison précoce afin de garantir un suivi correct des dépendances et de prévenir les vulnérabilités liées aux liaisons tardives, telles que la modification du chemin de recherche pendant l'exécution. Il est recommandé d'utiliser la `BEGIN ATOMIC` syntaxe des fonctions SQL afin de permettre la validation au moment de la compilation (c'est-à-dire la liaison anticipée) et la gestion des dépendances.

```
-- Example - Secure masking function with early binding
CREATE OR REPLACE FUNCTION secure_mask_ssn(input_ssn TEXT)
    RETURNS TEXT
    LANGUAGE SQL
    IMMUTABLE PARALLEL SAFE STRICT
    BEGIN ATOMIC
        SELECT CASE
            WHEN input_ssn IS NULL THEN NULL
            WHEN length(input_ssn) < 4 THEN repeat('X', length(input_ssn))
            ELSE repeat('X', length(input_ssn) - 4) || right(input_ssn, 4)
        END;
    END;
```

Vous pouvez également créer des fonctions immunisées contre les modifications du chemin de recherche en qualifiant explicitement toutes les références d'objets selon le schéma, afin de garantir un comportement cohérent entre les différentes sessions utilisateur.

```
-- Function immune to search path changes
CREATE OR REPLACE FUNCTION data_masking.secure_phone_mask(phone_number TEXT)
    RETURNS TEXT
    LANGUAGE SQL
    IMMUTABLE PARALLEL SAFE STRICT
    AS $$
    SELECT CASE
        WHEN phone_number IS NULL THEN NULL
        WHEN public.length(public.regexp_replace(phone_number, '[^0-9]', '', 'g')) < 10 THEN 'XXX-XXX-XXXX'
        ELSE public.regexp_replace(
            phone_number,
            '([0-9]{3})[0-9]{3}([0-9]{4})',
            public.concat('\1-XXX-\2')
        )
    END;
    $$;
```

Implémentez la validation des entrées dans les fonctions de masquage afin de gérer les cas extrêmes et d'éviter les comportements inattendus. Incluez toujours la gestion des valeurs NULL et validez les formats d'entrée pour garantir un comportement de masquage cohérent. 

```
-- Robust masking function with comprehensive input validation
CREATE OR REPLACE FUNCTION secure_mask_phone(phone_number TEXT)
    RETURNS TEXT
    LANGUAGE SQL
    IMMUTABLE PARALLEL SAFE STRICT
    BEGIN ATOMIC
        SELECT CASE
            WHEN phone_number IS NULL THEN NULL
            WHEN length(trim(phone_number)) = 0 THEN phone_number
            WHEN length(regexp_replace(phone_number, '[^0-9]', '', 'g')) < 10 THEN 'XXX-XXX-XXXX'
            ELSE regexp_replace(phone_number, '([0-9]{3})[0-9]{3}([0-9]{4})', '\1-XXX-\2')
        END;
    END;
```

## Comportement des déclencheurs DML avec pg\$1columnmask
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.DMLTriggerBehavior"></a>

Pour les déclencheurs de tables, les tables de transition seront entièrement démasquées. Pour les déclencheurs de vue (IOT), les tables de transition seront masquées en fonction des autorisations de consultation de l'utilisateur actuel.

Déclencheurs de table avec pg\$1columnmask  
Les déclencheurs reçoivent une table de transition contenant l'ancienne et la nouvelle version des lignes modifiées par la requête DML de lancement. Selon le moment où le déclencheur est déclenché, Aurora PostgreSQL remplit les anciennes et les nouvelles lignes. Par exemple, un `BEFORE INSERT` déclencheur contient uniquement les nouvelles versions des lignes et les anciennes versions vides, car il n'existe aucune ancienne version à référencer.  
`pg_columnmask`ne masque pas les tables de transition dans les déclencheurs des tables. Les déclencheurs peuvent utiliser des colonnes masquées à l'intérieur de leur corps et celui-ci voit les données non masquées. Le créateur du déclencheur doit s'assurer de la manière dont le déclencheur est exécuté pour un utilisateur. L'exemple suivant fonctionne correctement dans ce cas.  

```
-- Example for table trigger uses masked column in its definition
-- Create a table and insert some rows
CREATE TABLE public.credit_card_table (
    name TEXT,
    credit_card_no VARCHAR(16),
    is_fraud BOOL
);

INSERT INTO public.credit_card_table (name, credit_card_no, is_fraud)
    VALUES
    ('John Doe', '4532015112830366', false),
    ('Jane Smith', '5410000000000000', true),
    ('Brad Smith', '1234567891234567', true);

-- Create a role which will see masked data and grant it privileges
CREATE ROLE intern_user;
GRANT SELECT, DELETE ON public.credit_card_table TO intern_user;

-- Trigger which will silenty skip delete of non fraudelent credit cards
CREATE OR REPLACE FUNCTION prevent_non_fraud_delete()
    RETURNS TRIGGER AS
    $$
    BEGIN
        IF OLD.is_fraud = false THEN
            RETURN NULL;
        END IF;
        RETURN OLD;
    END;
    $$ LANGUAGE plpgsql;

CREATE TRIGGER prevent_non_fraud_delete
    BEFORE DELETE ON credit_card_table
    FOR EACH ROW
    EXECUTE FUNCTION prevent_non_fraud_delete();

CREATE OR REPLACE FUNCTION public.return_false()
    RETURNS BOOLEAN
    LANGUAGE SQL
    IMMUTABLE PARALLEL SAFE STRICT
    BEGIN ATOMIC
      SELECT false;
    END;

-- A masking policy that masks both credit card number and is_fraud column.
-- If we apply masking inside trigger then prevent_non_fraud_delete trigger will
-- allow deleting more rows to masked user (even non fraud ones).
CALL pgcolumnmask.create_masking_policy(
    'mask_credit_card_no_&_is_fraud'::NAME,
    'public.credit_card_table'::REGCLASS,
    JSON_BUILD_OBJECT('credit_card_no', 'pgcolumnmask.mask_text(credit_card_no)',
                      'is_fraud', 'public.return_false()')::JSONB,
    ARRAY['intern_user']::NAME[],
    10::INT
);

-- Test trigger behaviour using intern_user
BEGIN;
SET ROLE intern_user;
-- credit card number & is_fraud is completely masked from intern_user
SELECT * FROM public.credit_card_table;
    name    |  credit_card_no  | is_fraud 
------------+------------------+----------
 John Doe   | XXXXXXXXXXXXXXXX | f
 Jane Smith | XXXXXXXXXXXXXXXX | f
 Brad Smith | XXXXXXXXXXXXXXXX | f
(3 rows)

-- The delete trigger lets the intern user delete rows for Jane and Brad even though
-- intern_user sees their is_fraud = false, but the table trigger works with original
-- unmasked value
DELETE FROM public.credit_card_table RETURNING *;
    name    |  credit_card_no  | is_fraud 
------------+------------------+----------
 Jane Smith | XXXXXXXXXXXXXXXX | f
 Brad Smith | XXXXXXXXXXXXXXXX | f
(2 rows)

COMMIT;
```
Le créateur du déclencheur divulgue des données démasquées à l'utilisateur s'il ne fait pas attention aux instructions qu'il utilise dans le corps de son déclencheur. Par exemple, l'utilisation d'un `RAISE NOTICE ‘%’, masked_column;` imprime la colonne à l'utilisateur actuel.  

```
-- Example showing table trigger leaking column value to current user
CREATE OR REPLACE FUNCTION leaky_trigger_func()
    RETURNS TRIGGER AS
    $$
    BEGIN
        RAISE NOTICE 'Old credit card number was: %', OLD.credit_card_no;
        RAISE NOTICE 'New credit card number is %', NEW.credit_card_no;
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;

CREATE TRIGGER leaky_trigger
    AFTER UPDATE ON public.credit_card_table
    FOR EACH ROW
    EXECUTE FUNCTION leaky_trigger_func();

-- Grant update on column is_fraud to auditor role
-- auditor will NOT HAVE PERMISSION TO READ DATA
CREATE ROLE auditor;
GRANT UPDATE (is_fraud) ON public.credit_card_table TO auditor;

-- Also add auditor role to existing masking policy on credit card table
CALL pgcolumnmask.alter_masking_policy(
    'mask_credit_card_no_&_is_fraud'::NAME,
    'public.credit_card_table'::REGCLASS,
    NULL::JSONB,
    ARRAY['intern_user', 'auditor']::NAME[],
    NULL::INT
);

-- Log in as auditor
-- [auditor]
-- Update will fail if trying to read data from the table
UPDATE public.credit_card_table
    SET is_fraud = true
    WHERE credit_card_no = '4532015112830366';
ERROR:  permission denied for table cc_table

-- [auditor]
-- But leaky update trigger will still print the entire row even though
-- current user does not have permission to select from public.credit_card_table
UPDATE public.credit_card_table SET is_fraud = true;
NOTICE:  Old credit_card_no was: 4532015112830366
NOTICE:  New credit_card_no is 4532015112830366
```

Déclencheurs sur les vues avec pg\$1columnmask (au lieu de déclencheurs)  
Les déclencheurs ne peuvent être créés que sur des vues dans PostgreSQL. Ils sont utilisés pour exécuter des instructions DML sur des vues qui ne sont pas modifiables. Les tables de transit sont toujours masquées à l'intérieur au lieu d'un déclencheur (IOT), car la vue et les tables de base utilisées dans la requête de vue peuvent avoir des propriétaires différents. Dans ce cas, les tables de base peuvent avoir certaines politiques de masquage applicables au propriétaire de la vue et le propriétaire de la vue doit toujours voir les données masquées des tables de base dans ses déclencheurs. Cela est différent des déclencheurs sur les tables car dans ce cas, le créateur du déclencheur et les données contenues dans les tables appartiennent au même utilisateur, ce qui n'est pas le cas ici.  

```
-- Create a view over credit card table
CREATE OR REPLACE VIEW public.credit_card_view
    AS
    SELECT * FROM public.credit_card_table;

-- Truncate credit card table and insert fresh data
TRUNCATE TABLE public.credit_card_table;
INSERT INTO public.credit_card_table (name, credit_card_no, is_fraud)
    VALUES
    ('John Doe', '4532015112830366', false),
    ('Jane Smith', '5410000000000000', true),
    ('Brad Smith', '1234567891234567', true);

CREATE OR REPLACE FUNCTION public.print_changes()
    RETURNS TRIGGER AS
    $$
    BEGIN
        RAISE NOTICE 'Old row: name=%, credit card number=%, is fraud=%',
            OLD.name, OLD.credit_card_no, OLD.is_fraud;
    
        RAISE NOTICE 'New row: name=%, credit card number=%, is fraud=%',
            NEW.name, NEW.credit_card_no, NEW.is_fraud;
    
    RETURN NEW;
   END;
   $$ LANGUAGE plpgsql;

CREATE TRIGGER print_changes_trigger
    INSTEAD OF UPDATE ON public.credit_card_view
    FOR EACH ROW
    EXECUTE FUNCTION public.print_changes();

GRANT SELECT, UPDATE ON public.credit_card_view TO auditor;

-- [auditor]
-- Login as auditor role
BEGIN;

-- Any data coming out from the table will be masked in instead of triggers
-- according to masking policies applicable to current user
UPDATE public.credit_card_view
    SET name = CONCAT(name, '_new_name')
    RETURNING *;
NOTICE:  Old row: name=John Doe, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=John Doe_new_name, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  Old row: name=Jane Smith, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=Jane Smith_new_name, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  Old row: name=Brad Smith, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=Brad Smith_new_name, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
        name         |  credit_card_no  | is_fraud 
---------------------+------------------+----------
 John Doe_new_name   | XXXXXXXXXXXXXXXX | f
 Jane Smith_new_name | XXXXXXXXXXXXXXXX | f
 Brad Smith_new_name | XXXXXXXXXXXXXXXX | f
 
 -- Any new data going into the table using INSERT or UPDATE command will be unmasked
 UPDATE public.credit_card_view
    SET credit_card_no = '9876987698769876'
    RETURNING *;
NOTICE:  Old row: name=John Doe, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=John Doe, credit card number=9876987698769876, is fraud=f
NOTICE:  Old row: name=Jane Smith, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=Jane Smith, credit card number=9876987698769876, is fraud=f
NOTICE:  Old row: name=Brad Smith, credit card number=XXXXXXXXXXXXXXXX, is fraud=f
NOTICE:  New row: name=Brad Smith, credit card number=9876987698769876, is fraud=f
    name    |  credit_card_no  | is_fraud 
------------+------------------+----------
 John Doe   | 9876987698769876 | f
 Jane Smith | 9876987698769876 | f
 Brad Smith | 9876987698769876 | f
 
 COMMIT;
```

Niveau de base de données/utilisateur GuCs pour contrôler le comportement des déclencheurs  
Deux paramètres de configuration contrôlent le comportement d'exécution des déclencheurs pour les utilisateurs dotés de politiques de masquage applicables. Utilisez ces paramètres pour empêcher les déclencheurs de s'exécuter sur des tables ou des vues masquées lorsque des restrictions de sécurité supplémentaires sont requises. Les deux paramètres sont désactivés par défaut, ce qui permet aux déclencheurs de s'exécuter normalement.  
**Premier GUC : restriction de déclenchement sur les tables masquées**  
Spécifications :  
+ Nom : `pgcolumnmask.restrict_dml_triggers_for_masked_users`
+ Type : `boolean`
+ Par défaut : `false` (les déclencheurs sont autorisés à être exécutés)
Empêche l'exécution du déclencheur sur les tables masquées pour les utilisateurs masqués lorsque ce paramètre est défini sur TRUE. `pg_columnmask`corrige l'erreur.  
**Deuxième GUC : restriction du déclenchement du déclenchement sur les vues avec des tables masquées**  
Spécifications :  
+ Nom : `pgcolumnmask.restrict_iot_triggers_for_masked_users`
+ Type : `boolean`
+ Par défaut : `false` (les déclencheurs sont autorisés à être exécutés)
Empêche l'exécution de déclencheurs sur les vues qui incluent des tables masquées dans leur définition pour les utilisateurs masqués lorsqu'elles sont définies sur TRUE.

Ces paramètres fonctionnent indépendamment et sont configurables comme les paramètres de configuration de base de données standard.

# Scénarios de déplacement de données pg\$1columnmask dans Aurora PostgreSQL
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement"></a>

`pg_columnmask`le comportement varie selon les différentes opérations de déplacement de données selon que l'opération a lieu au niveau de la couche de stockage, de la logique ou de l'application. Les opérations au niveau du stockage (telles que le clonage) se comportent différemment des opérations logiques (telles que`pg_dump`) et des opérations au niveau des applications (telles que les requêtes FDW). Cette section décrit le comportement de masquage pour les scénarios courants, notamment la réplication, les sauvegardes, les exportations et les migrations, et explique les implications de sécurité de chacun d'entre eux.

**Topics**
+ [Base de données globale Aurora et répliques de lecture](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.RR)
+ [Clone de base de données et restauration de snapshots](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Clones)
+ [Réplication logique](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.LogRep)
+ [Déploiements bleu/vert](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.BlueGreen)
+ [Streams Zero-ETL et CDC](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.ZETL)
+ [AWS Database Migration Service](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DMS)
+ [Exportations de données](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DataExport)
+ [Vues et vues matérialisées](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Views)
+ [Déchargement et restauration de données](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DDR)
+ [Enveloppeur de données étrangères](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.FDQ)

## Base de données globale Aurora et répliques de lecture
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.RR"></a>

Les `pg_columnmask` politiques Aurora sont stockées dans les tables du système de base de données au sein du volume du cluster. Toutes les répliques ont accès aux mêmes politiques et renvoient des résultats systématiquement masqués. Pour les déploiements de la base de données globale Aurora, les `pg_columnmask` politiques sont répliquées vers les tables secondaires Régions AWS ainsi que vers les autres tables du système de base de données, garantissant ainsi une protection des données cohérente dans toutes les régions. Pendant les scénarios de basculement, toutes les `pg_columnmask` politiques restent intactes et fonctionnelles.

## Clone de base de données et restauration de snapshots
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Clones"></a>

Les opérations de clonage rapide et de restauration de snapshots d'Aurora préservent toutes les `pg_columnmask` politiques, tous les rôles et toutes les configurations dans les tables du système de base de données. La base de données clonée ou restaurée hérite de toutes les politiques existantes du cluster source. Après le clonage ou la restauration, chaque cluster de bases de données applique des `pg_columnmask` politiques indépendantes.

## Réplication logique
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.LogRep"></a>

Lors de la synchronisation initiale, la réplication logique utilise des opérations SQL COPY standard, et `pg_columnmask` les politiques sont appliquées en fonction des autorisations de l'utilisateur de réplication. Pendant le CDC (capture des données de modification) en cours, les politiques de masquage ne sont pas appliquées et les données démasquées sont répliquées via des enregistrements WAL. Les utilisateurs dotés de `pg_create_subscription` privilèges peuvent potentiellement exfiltrer des données non masquées en configurant la réplication vers un système qu'ils contrôlent.

## Déploiements bleu/vert
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.BlueGreen"></a>

Lors de la restauration des instantanés, les `pg_columnmask` politiques sont automatiquement incluses. L'environnement vert commence par une copie identique de toutes les politiques de l'environnement bleu. Lors de la réplication du bleu au vert, les données ne sont pas masquées. Les modifications ultérieures de la politique de masquage (commandes DDL) sur le cluster bleu ne sont pas répliquées sur le cluster vert et invalident les déploiements RDS. blue/green 

## Streams Zero-ETL et CDC
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.ZETL"></a>

La réplication des données n'est pas affectée par `pg_columnmask` les politiques. Zero-ETL prend en charge la réplication DDL mais ne réplique `pg_columnmask` pas les politiques RLS. Aucune politique de masquage n'est appliquée aux données répliquées dans Zero-ETL.

## AWS Database Migration Service
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DMS"></a>

La synchronisation initiale des données est masquée ou démasquée en fonction de l'utilisateur sélectionné pour la tâche DMS. Les données du CDC sont toujours démasquées. Bien que les politiques RLS internes `pg_columnmask` associées puissent être migrées, elles ne fonctionneront pas sur les cibles non activées par pg\$1columnmask.

## Exportations de données
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DataExport"></a>

`pg_columnmask`traite les exportations comme toute autre opération de requête : le masquage est appliqué en fonction des autorisations de l'utilisateur exécutant. Cela s'applique aux commandes SQL telles que COPY, SELECT INTO, CREATE TABLE AS et à la fonctionnalité d'exportation S3 d'Aurora PostgreSQL. 

**Note**  
Lorsque des utilisateurs masqués exportent des données, les fichiers qui en résultent contiennent des valeurs masquées susceptibles de violer les contraintes de la base de données lors de la restauration.

## Vues et vues matérialisées
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Views"></a>

Tenez compte des considérations suivantes lorsque vous utilisez des vues :
+ **Vues régulières** : utilisez toujours la `INVOKER` sémantique. Les politiques de masquage de l'utilisateur actuel s'appliquent lors de l'interrogation de la vue, quel que soit le créateur de la vue.
+ **Vues matérialisées** : lors de l'actualisation, ce sont les politiques de masquage du propriétaire de la vue matérialisée qui s'appliquent, et non celles de l'utilisateur effectuant l'actualisation. Si le propriétaire applique des politiques de masquage, la vue matérialisée contient toujours des données masquées.

## Déchargement et restauration de données
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DDR"></a>

`pg_dump`fonctionne comme un utilisateur normal de la base de données et applique des politiques de masquage en fonction des autorisations de l'utilisateur qui se connecte. Si un utilisateur masqué effectue un vidage, le fichier de sauvegarde contient des données masquées. `pg_columnmask`les politiques sont incluses dans le dump en tant que partie intégrante du schéma de base de données. Une restauration réussie nécessite que tous les rôles référencés existent dans la base de données cible et que l'`pg_columnmask`extension soit installée sur la cible.

**Note**  
À partir de PostgreSQL 18`pg_dump`, prend en charge `—no-policies` l'option qui exclut à la fois la sécurité au niveau des lignes (RLS) `pg_columnmask` et les politiques de masquage des vidages de bases de données. Pour plus d'informations, consultez [pg\$1dump](https://www.postgresql.org/docs/current/app-pgdump.html).

## Enveloppeur de données étrangères
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.FDQ"></a>

Lorsque vous utilisez des enveloppeurs de données étrangers, les politiques de masquage sur les tables distantes sont appliquées en fonction des autorisations de l'utilisateur mappé sur le serveur source, et non des autorisations de l'utilisateur demandeur local. Bien que vous puissiez accéder aux données distantes masquées via FDW, vous ne pouvez pas créer de politiques DDM ou RLS directement sur les tables étrangères de votre base de données locale.