

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Menggunakan masking dinamis dengan Aurora PostgreSQL
<a name="AuroraPostgreSQL.Security.DynamicMasking"></a>

Dynamic data masking adalah fitur keamanan yang melindungi data sensitif dalam database Aurora PostgreSQL dengan mengontrol bagaimana data muncul kepada pengguna pada waktu query. Aurora mengimplementasikannya melalui ekstensi. `pg_columnmask` `pg_columnmask`menyediakan perlindungan data tingkat kolom yang melengkapi keamanan tingkat baris asli PostgreSQL dan mekanisme kontrol akses granular.

Dengan`pg_columnmask`, Anda membuat kebijakan masking yang menentukan visibilitas data berdasarkan peran pengguna. Saat pengguna menanyakan tabel dengan kebijakan masking, Aurora PostgreSQL menerapkan fungsi masking yang sesuai pada waktu kueri berdasarkan peran pengguna dan bobot kebijakan. Data yang mendasarinya tetap tidak berubah dalam penyimpanan.

`pg_columnmask`mendukung kemampuan berikut:
+ Fungsi **masking bawaan dan kustom - Gunakan fungsi** pra-bangun untuk pola umum seperti email dan masking teks, atau buat fungsi kustom Anda sendiri untuk melindungi data sensitif (PII) melalui kebijakan masking berbasis SQL.
+ **Beberapa strategi masking** — Sembunyikan informasi sepenuhnya, ganti nilai paral dengan wildcard, atau tentukan pendekatan masking khusus.
+ **Prioritas kebijakan** - Tentukan beberapa kebijakan untuk satu kolom. Gunakan bobot untuk menentukan kebijakan masking mana yang harus digunakan saat beberapa kebijakan berlaku pada kolom. Aurora PostgreSQL menerapkan kebijakan berdasarkan bobot dan keanggotaan peran pengguna. 

`pg_columnmask`tersedia di Aurora PostgreSQL versi 16.10 dan lebih tinggi, dan versi 17.6 dan lebih tinggi. Tersedia tersedia tanpa biaya tambahan.

# Memulai dengan masking dinamis
<a name="AuroraPostgreSQL.Security.DynamicMasking.GetStarted"></a>

Untuk menutupi data secara dinamis, Anda menginstal `pg_columnmask` ekstensi di database Anda dan membuat kebijakan masking untuk tabel Anda. Proses penyiapan melibatkan verifikasi prasyarat, instalasi ekstensi, konfigurasi peran, pembuatan kebijakan, dan pengujian validasi.

## Instalasi dan konfigurasi ekstensi
<a name="AuroraPostgreSQL.Security.DynamicMasking.GetStarted.Installation"></a>

Connect ke cluster Aurora PostgreSQL Anda menggunakan RDS Console Query Editor atau klien PostgreSQL seperti psql dengan kredensial rds\$1superuser (pengguna utama).

Jalankan perintah pembuatan ekstensi untuk mengaktifkan `pg_columnmask` fungsionalitas:

```
CREATE EXTENSION pg_columnmask;
```

Perintah ini menginstal `pg_columnmask` ekstensi, membuat tabel katalog yang diperlukan, dan mendaftarkan fungsi masking bawaan. Instalasi ekstensi khusus basis data, artinya Anda harus menginstalnya secara terpisah di setiap database di mana fungsionalitas diperlukan.

**catatan**  
Koneksi yang dibuat sebelum menginstal ekstensi ini masih akan menampilkan data yang dibuka kedoknya. Tutup dan sambungkan kembali untuk memperbaikinya.

Verifikasi instalasi ekstensi dengan memeriksa fungsi masking yang tersedia:

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

# Prosedur untuk mengelola kebijakan penyembunyian data
<a name="AuroraPostgreSQL.Security.DynamicMasking.Procedures"></a>

Anda dapat mengelola kebijakan masking menggunakan prosedur yang disediakan oleh `pg_columnmask` ekstensi. Untuk membuat, memodifikasi, atau menghapus kebijakan masking, Anda harus memiliki salah satu hak istimewa berikut:
+ Pemilik tabel tempat Anda membuat `pg_columnmask` kebijakan.
+ Anggota dari`rds_superuser`.
+ Anggota peran manajer `pg_columnmask` kebijakan yang ditetapkan oleh `pgcolumnmask.policy_admin_rolname` parameter.

Perintah berikut membuat tabel yang digunakan di bagian berikutnya:

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

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

Prosedur berikut membuat kebijakan masking baru untuk tabel pengguna:

**Sintaksis**

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

**Argumen**


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nama kebijakan masking. Harus unik per tabel.  | 
| table\$1name | REGCLASS |   qualified/unqualified Nama atau oid tabel untuk menerapkan kebijakan masking.  | 
| masking\$1expressions | JSONB |  Objek JSON yang berisi nama kolom dan pasangan fungsi masking. Setiap kunci adalah nama kolom dan nilainya adalah ekspresi masking yang akan diterapkan pada kolom itu.  | 
| roles | NAMA [] |  Peran yang diterapkan kebijakan masking ini. Defaultnya adalah PUBLIK.  | 
| weight | INT |  Bobot kebijakan masking. Jika beberapa kebijakan berlaku untuk kueri pengguna tertentu, kebijakan dengan bobot tertinggi (bilangan bulat lebih tinggi) akan diterapkan ke setiap kolom bertopeng. Default-nya adalah 0. Tidak ada dua kebijakan masking di atas meja yang dapat memiliki kekuatan yang sama.  | 

**Jenis pengembalian**

Tidak ada

**Example membuat kebijakan masking yang menutupi kolom email untuk `test_user` peran tersebut:**  

```
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>

Prosedur ini memodifikasi kebijakan masking yang ada. `ALTER_MASKING_POLICY`dapat memodifikasi ekspresi masking kebijakan, serangkaian peran yang diterapkan kebijakan dan bobot kebijakan masking. Ketika salah satu parameter tersebut dihilangkan, bagian yang sesuai dari kebijakan tidak berubah.

**Sintaksis**

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

**Argumen**


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nama kebijakan masking yang ada.  | 
| table\$1name | REGCLASS |   qualified/unqualified Nama oid dari tabel yang berisi kebijakan masking.  | 
| masking\$1expressions | JSONB |  Objek JSON baru yang berisi nama kolom dan pasangan fungsi masking atau NULL sebaliknya.  | 
| roles | NAMA [] |  Daftar peran baru yang diterapkan kebijakan penyembunyian ini atau NULL sebaliknya.  | 
| weight | INT |  Bobot baru untuk kebijakan masking atau NULL sebaliknya.  | 

**Jenis pengembalian**

Tidak ada

**Example menambahkan peran analis ke kebijakan masking yang ada tanpa mengubah atribut kebijakan lainnya.**  

```
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>

Prosedur ini menghapus kebijakan masking yang ada.

**Sintaksis**

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

**Argumen**


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| policy\$1name | NAME |  Nama kebijakan masking yang ada.  | 
| table\$1name | REGCLASS |   qualified/unqualified Nama oid dari tabel yang berisi kebijakan masking.  | 

**Jenis pengembalian**

Tidak ada

**Example menjatuhkan kebijakan masking customer\$1mask**  

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

# Melarikan diri dari pengidentifikasi dalam prosedur DDL kebijakan masking
<a name="AuroraPostgreSQL.Security.DynamicMasking.EscapeIdentifiers"></a>

Saat membuat kebijakan penyembunyian data dengan pengidentifikasi yang dikutip, pelolosan yang tepat diperlukan untuk memastikan referensi objek dan aplikasi kebijakan yang benar. Untuk menggunakan pengenal yang dikutip dalam prosedur manajemen kebijakan `pg_columnmask` masking:
+ **Nama kebijakan** — Harus dilampirkan dalam tanda kutip ganda.
+ **Nama tabel** - Baik nama skema dan nama tabel harus dilampirkan dalam tanda kutip ganda satu per satu bila diperlukan.
+ **Ekspresi masking** — Nama kolom dan fungsi dalam ekspresi masking harus diapit tanda kutip ganda dan tanda kutip itu sendiri harus diloloskan menggunakan garis miring terbalik.
+ **Peran** - Array nama peran dikutip secara otomatis. Nama peran harus sama persis dengan nama seperti yang terlihat di `pg_roles` termasuk sensitivitas kasus.

**Example dari melarikan diri dan mengutip sintaks**  
Contoh ini menunjukkan sintaks escaping dan kutipan yang tepat saat membuat kebijakan masking untuk tabel, kolom, fungsi, dan peran yang menggunakan nama kasus campuran atau memerlukan pengidentifikasi yang dikutip di 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
```

## Pandangan administratif
<a name="AuroraPostgreSQL.Security.DynamicMasking.AdminViews"></a>

Anda dapat meninjau semua `pg_columnmask` kebijakan menggunakan tampilan `pgcolumnmask.pg_columnmask_policies` administratif yang dapat diakses publik. Informasi berikut tersedia menggunakan tampilan ini. Tampilan hanya menampilkan kebijakan masking yang dimiliki oleh pengguna saat ini.


| Nama kolom | Jenis data | Deskripsi | 
| --- | --- | --- | 
|  nama skema  | NAME |  Skema hubungan dengan mana kebijakan dilampirkan  | 
|  tablename  | NAME |  Nama hubungan yang dilampirkan kebijakan  | 
|  nama kebijakan  | NAME |  Nama kebijakan masking, semua kebijakan masking memiliki nama unik  | 
|  peran  | TEKS [] |  Peran kebijakan yang berlaku.  | 
|  bertopeng\$1kolom  | TEKS [] |  Kolom bertopeng  | 
|  masking\$1functions  | TEKS [] |  Fungsi masking  | 
| berat | INT |  Bobot kebijakan terlampir  | 

# Fungsi penyembunyian data yang telah ditentukan sebelumnya
<a name="AuroraPostgreSQL.Security.DynamicMasking.PredefinedMaskingFunctions"></a>

`pg_columnmask`ekstensi menyediakan fungsi utilitas bawaan yang ditulis dalam bahasa C (untuk eksekusi lebih cepat) yang dapat digunakan sebagai ekspresi masking untuk `pg_columnmask` kebijakan.

**mask\$1text**

Fungsi untuk menutupi data teks dengan opsi visibilitas yang dapat dikonfigurasi.

**Argumen**


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| input | TEXT |  String teks asli yang akan disamarkan  | 
| mask\$1char | ARANG (1) |  Karakter yang digunakan untuk masking (default: 'X')  | 
| visible\$1prefix | INT |  Jumlah karakter di awal teks masukan yang akan tetap terbuka kedoknya (default: 0)  | 
| visible\$1suffix | INT |  Jumlah karakter di akhir teks masukan yang akan tetap terbuka kedoknya (default: 0)  | 
| use\$1hash\$1mask | BOOLEAN |  Jika TRUE, gunakan masking berbasis hash alih-alih mask\$1char (default: FALSE)  | 

**Example menggunakan opsi masking yang berbeda**  
Tutupi seluruh string input dengan karakter 'X' default  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World');
  mask_text  
-------------
 XXXXXXXXXXX
```
Gunakan `mask_char` argumen untuk menutupi masukan teks menggunakan karakter yang berbeda  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World', '*');
  mask_text  
-------------
 ***********
```
Gunakan `visible_prefix` dan `visible_suffix` parameter untuk mengontrol berapa banyak karakter yang tetap dibuka kedoknya di awal dan akhir teks  

```
postgres=> SELECT pgcolumnmask.mask_text('Hello World', '*', 5, 1);
  mask_text  
-------------
 Hello*****d
```
Kapan `use_hash_mask` benar, string input ditutupi menggunakan `mask_char` argumen karakter acak diabaikan tetapi `visible_prefix` dan `visible_suffix` masih dihormati  

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

**mask\$1timestamp**


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| ts\$1to\$1mask | TIMESTAMP |  Stempel waktu asli yang akan ditutup  | 
| mask\$1part | TEXT |  Menentukan bagian mana dari stempel waktu untuk menutupi (default: 'all') Nilai yang valid: 'year', 'month', 'day', 'hour', 'minute', 'second', 'all'  | 
| mask\$1value | TIMESTAMP |  Nilai stempel waktu yang digunakan untuk masking (default: '1900-01-01 00:00:00 ')  | 

**Example menggunakan `mask_timestamps`**  
Contoh-contoh ini menunjukkan masking stempel waktu lengkap ke nilai default, penyembunyian sebagian komponen stempel waktu tertentu (hanya tahun), dan penyembunyian dengan nilai penggantian khusus.  
Sepenuhnya menutupi nilai input ke stempel waktu default  

```
postgres=> SELECT pgcolumnmask.mask_timestamp('2023-06-15 14:30:00');
   mask_timestamp    
---------------------
 1900-01-01 00:00:00
```
Untuk menutupi hanya satu bagian stempel waktu dari contoh hanya tahun  

```
postgres=> SELECT pgcolumnmask.mask_timestamp('2023-06-15 14:30:00', 'year');
   mask_timestamp    
---------------------
 1900-06-15 14:30:00
```
Untuk mengubah nilai masked untuk stempel waktu gunakan argumen `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**

Fungsi untuk menutupi alamat email sambil mempertahankan struktur email.


| Parameter | JenisData | Deskripsi | 
| --- | --- | --- | 
| input | TEXT |  Alamat email asli yang akan disamarkan  | 
| mask\$1char | ARANG (1) |  Karakter yang digunakan untuk masking (default: 'X')  | 
| mask\$1local | BOOLEAN |  Jika TRUE, menutupi bagian lokal email (sebelum @) (default: TRUE)  | 
| mask\$1domain | BOOLEAN |  Jika TRUE, menutupi bagian domain email (setelah @) (default: TRUE)  | 

**Example menggunakan `mask_email`**  
Contoh-contoh ini menunjukkan masking email lengkap, karakter topeng kustom, dan penyembunyian selektif dari bagian lokal atau bagian domain dari alamat email.  
Masking lengkap  

```
postgres=> SELECT pgcolumnmask.mask_email('user@example.com');
    mask_email    
------------------
 XXXX@XXXXXXX.com
```
Gunakan `mask_char` untuk mengubah karakter yang digunakan untuk masking  

```
postgres=> SELECT pgcolumnmask.mask_email('user@example.com', '*');
    mask_email    
------------------
 ****@*******.com
```
Gunakan `mask_local` dan `mask_domain` untuk mengontrol masking pada lokal dan domain  

```
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
```

# Menerapkan pg\$1columnmask dalam alur kerja end-to-end
<a name="AuroraPostgreSQL.Security.DynamicMasking.WorkflowExample"></a>

Bagian ini menunjukkan implementasi lengkap `pg_columnmask` menggunakan tabel karyawan sampel dengan data sensitif. Anda akan belajar cara membuat fungsi masking khusus, menentukan beberapa kebijakan masking dengan tingkat bobot yang berbeda untuk berbagai peran (magang, dukungan, analis), dan mengamati bagaimana pengguna dengan keanggotaan peran tunggal atau ganda melihat berbagai tingkat data bertopeng. Contohnya juga mencakup perilaku penyembunyian dalam pernyataan DHTML dengan klausa RETURNING, pemicu pada tabel versus tampilan, dan operasi manajemen kebijakan termasuk mengganti nama, mengubah bobot, dan pembersihan.

1. Buat tabel sampel dengan beberapa data sensitif:

   ```
   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. Buat fungsi masking kustom:

   ```
   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. Buat beberapa kebijakan dengan tingkat masking yang berbeda berdasarkan peran pengguna:

   ```
   -- 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. Contoh berikut menunjukkan bagaimana pengguna yang berbeda melihat data berdasarkan keanggotaan peran dan bobot kebijakan mereka.

   ```
   -- 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;
   ```

   Sebagai magang (masking ketat):

   ```
   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
   ```

   Sebagai pengguna pendukung (masking moderat):

   ```
   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
   ```

   Sebagai seorang analis (masking paling ringan):

   ```
   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
   ```

   Sebagai pengguna ethan\$1support\$1intern yang merupakan pengguna magang dan mendukung:

   ```
   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
   ```

   Sebagai john\$1analyst\$1intern yang merupakan magang dan analis:

   ```
   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
   ```

# Memahami perilaku masking dalam operasi DHTML
<a name="AuroraPostgreSQL.Security.DynamicMasking.DMLMasking"></a>

`pg_columnmask`berlaku secara konsisten di semua operasi DHTML, termasuk pernyataan INSERT, UPDATE, DELETE, dan MERGE. Saat Anda menjalankan operasi ini, Aurora PostgreSQL menutupi data sesuai dengan prinsip inti — data apa pun yang dibaca dari penyimpanan ditutupi sesuai dengan kebijakan yang berlaku pengguna saat ini.

Masking mempengaruhi beberapa komponen query berikut seperti:
+ Klausul WHERE
+ Ketentuan BERGABUNG
+ Subkueri
+ Klausul PENGEMBALIAN

Semua komponen ini beroperasi pada nilai bertopeng, bukan data asli. Sementara data ditulis ke penyimpanan tanpa kedok, pengguna hanya melihat tampilan bertopeng mereka saat membacanya kembali.

Aurora PostgreSQL memberlakukan semua kendala database (BUKAN NULL, UNIQUE, CHECK, FOREIGN KEY) pada nilai tersimpan aktual, bukan nilai bertopeng. Ini kadang-kadang dapat menciptakan inkonsistensi yang nyata jika fungsi masking tidak dirancang dengan hati-hati.

Masking berfungsi bersama izin tingkat kolom:
+ Pengguna tanpa hak SELECT tidak dapat membaca kolom
+ Pengguna dengan hak istimewa SELECT melihat nilai bertopeng sesuai dengan kebijakan yang berlaku

# Memahami perilaku masking dalam fungsi pemicu
<a name="AuroraPostgreSQL.Security.DynamicMasking.TriggerFunctionMasking"></a>

Ketika `pg_columnmask` kebijakan diterapkan ke tabel, penting untuk memahami bagaimana masking berinteraksi dengan fungsi pemicu. Pemicu adalah fungsi database yang dijalankan secara otomatis sebagai respons terhadap peristiwa tertentu di atas meja, seperti operasi INSERT, UPDATE, atau DELETE.

Secara default, DDM menerapkan aturan masking yang berbeda tergantung pada jenis pemicu:

Pemicu tabel  
**Tabel transisi dibuka kedoknya** - Fungsi pemicu pada tabel memiliki akses ke data yang dibuka kedok dalam tabel transisi mereka untuk versi baris lama dan baru  
Pemilik tabel membuat pemicu dan memiliki data, sehingga mereka memiliki akses penuh untuk mengelola tabel mereka secara efektif

Lihat Pemicu (BUKAN Pemicu)  
**Tabel transisi disamarkan** - Fungsi pemicu pada tampilan melihat data bertopeng sesuai dengan izin pengguna saat ini  
Pemilik tampilan mungkin berbeda dari pemilik tabel dasar dan harus menghormati kebijakan masking pada tabel yang mendasarinya

Dua parameter konfigurasi tingkat server mengontrol perilaku pemicu dengan tabel bertopeng. Ini hanya dapat diatur oleh`rds_superuser`:
+ **Batasi Pemicu pada Tabel Bertopeng** — Mencegah eksekusi pemicu saat pengguna bertopeng melakukan operasi DHTML pada tabel dengan kebijakan masking yang berlaku.
+ **Batasi Pemicu pada Tampilan dengan Tabel Bertopeng:** — Mencegah eksekusi pemicu pada tampilan saat definisi tampilan menyertakan tabel dengan kebijakan masking yang berlaku untuk pengguna saat ini.

**Example perbedaan antara aplikasi fungsi ke tabel dan tampilan**  
Contoh berikut menciptakan fungsi pemicu yang mencetak nilai baris lama dan baru, kemudian menunjukkan bagaimana fungsi yang sama berperilaku berbeda ketika dilampirkan ke tabel versus tampilan.  

```
-- 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;
```
Sebaiknya tinjau perilaku pemicu sebelum menerapkan pemicu pada tabel bertopeng. Pemicu tabel memiliki akses ke data yang dibuka kedok dalam tabel transisi, sementara pemicu tampilan melihat data bertopeng.

**Example mengganti nama kebijakan masking**  
Contoh berikut menunjukkan cara mengganti nama kebijakan yang ada menggunakan prosedur. `rename_masking_policy`  

```
-- 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 mengubah bobot kebijakan**  
Contoh berikut menunjukkan bagaimana mengubah bobot kebijakan untuk mengubah bobotnya.  

```
-- 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 membersihkan**  
Contoh berikut menunjukkan bagaimana untuk menghapus semua kebijakan, tabel dan pengguna.  

```
-- 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;
```

# Mengkonfigurasi peran manajemen kebijakan masking
<a name="AuroraPostgreSQL.Security.DynamicMasking.PolicyManagementRole"></a>

Ekstensi masking kolom PostgreSQL`pg_columnmask`, memungkinkan Anda untuk mendelegasikan pengelolaan kebijakan masking ke peran tertentu, bukan mewajibkan atau hak istimewa pemilik tabel. `rds_superuser` Ini memberikan kontrol yang lebih terperinci atas siapa yang dapat membuat, mengubah, dan menjatuhkan kebijakan masking.

Untuk mengonfigurasi peran yang akan memiliki hak istimewa pengelolaan kebijakan masking, ikuti langkah-langkah berikut:

1. Membuat peran admin kebijakan — Sebagai`rds_superuser`, buat peran baru yang bertanggung jawab untuk mengelola kebijakan masking:

   ```
   CREATE ROLE mask_admin NOLOGIN;
   ```

1. Konfigurasikan parameter PostgreSQL - Di grup parameter cluster DB kustom Anda, atur `pgcolumnmask.policy_admin_rolname` parameter konfigurasi engine ke nama peran yang Anda buat:

   ```
   pgcolumnmask.policy_admin_rolname = mask_admin
   ```

   Parameter konfigurasi mesin ini dapat diatur dalam grup parameter cluster DB dan tidak memerlukan reboot instance. Untuk detail tentang memperbarui parameter, lihat[Memodifikasi parameter dalam grup parameter cluster DB di Amazon Aurora](USER_WorkingWithParamGroups.ModifyingCluster.md).

1. Berikan peran kepada pengguna Sebagai`rds_superuser`, berikan `mask_admin` peran tersebut kepada pengguna yang seharusnya dapat mengelola kebijakan masking:

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

   Selain itu, pastikan bahwa pengguna memiliki hak istimewa PENGGUNAAN pada skema tempat mereka akan mengelola kebijakan masking:

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

Sekarang, ketika pengguna `alice` dan `bob` terhubung ke database, mereka dapat menggunakan fungsi `pg_columnmask` ekstensi standar untuk membuat, mengubah, dan menghapus kebijakan masking pada semua tabel di semua skema di mana mereka memiliki `USAGE` hak istimewa pada skema.

# Praktik terbaik untuk implementasi pg\$1columnmask yang aman
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices"></a>

Bagian berikut menyediakan praktik terbaik keamanan untuk diterapkan `pg_columnmask` di lingkungan Aurora PostgreSQL Anda. Ikuti rekomendasi ini untuk:
+ Membangun arsitektur kontrol akses berbasis peran yang aman
+ Kembangkan fungsi masking yang mencegah kerentanan keamanan
+ Memahami dan mengontrol perilaku pemicu dengan data bertopeng

## Arsitektur keamanan berbasis peran
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.architecture"></a>

Tentukan hierarki peran untuk mengimplementasikan kontrol akses dalam database Anda. Aurora PostgreSQL `pg_columnmask` menambah kontrol ini dengan menyediakan lapisan tambahan untuk penyembunyian data berbutir halus dalam peran tersebut.

Buat peran khusus yang selaras dengan fungsi organisasi daripada memberikan izin kepada pengguna individu. Pendekatan ini memberikan auditabilitas yang lebih baik dan menyederhanakan manajemen izin saat struktur organisasi Anda berkembang.

**Example Menciptakan Heirarki Peran Organisasi**  
Contoh berikut membuat hierarki peran organisasi dengan peran khusus untuk fungsi yang berbeda, kemudian menetapkan pengguna individu ke peran yang sesuai. Dalam contoh ini, peran organisasi (analyst\$1role, support\$1role) dibuat terlebih dahulu, kemudian pengguna individu diberikan keanggotaan dalam peran ini. Struktur ini memungkinkan Anda mengelola izin di tingkat peran, bukan untuk setiap pengguna individu.  

```
-- 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;
```
Menerapkan prinsip hak istimewa terkecil dengan hanya memberikan izin minimum yang diperlukan untuk setiap peran. Hindari pemberian izin luas yang dapat dieksploitasi jika kredensyal dikompromikan.  

```
-- 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;
```
Administrator kebijakan memerlukan `USAGE` hak istimewa pada skema tempat mereka mengelola kebijakan penyembunyian. Berikan hak istimewa ini secara selektif, mengikuti prinsip hak istimewa paling sedikit. Lakukan tinjauan reguler atas izin akses skema untuk memastikan hanya personel yang berwenang yang mempertahankan kemampuan manajemen kebijakan.  
Konfigurasi parameter peran admin kebijakan dibatasi hanya untuk administrator database. Parameter ini tidak dapat diubah pada tingkat basis data atau sesi, mencegah pengguna yang tidak memiliki hak istimewa untuk mengganti penetapan admin kebijakan. Pembatasan ini memastikan bahwa kontrol kebijakan masking tetap terpusat dan aman.  
Tetapkan peran admin kebijakan untuk individu tertentu, bukan grup. Pendekatan yang ditargetkan ini memastikan akses selektif ke manajemen kebijakan masking, karena administrator kebijakan memiliki kemampuan untuk menutupi semua tabel dalam database. 

## Pengembangan fungsi masking yang aman
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.MaskingDevelopment"></a>

Kembangkan fungsi masking menggunakan semantik pengikatan awal untuk memastikan pelacakan ketergantungan yang tepat dan mencegah kerentanan pengikatan yang terlambat seperti modifikasi jalur pencarian selama runtime. Disarankan untuk menggunakan `BEGIN ATOMIC` sintaks untuk fungsi SQL untuk mengaktifkan validasi waktu kompilasi (yaitu pengikatan awal) dan manajemen ketergantungan.

```
-- 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;
```

Atau, buat fungsi yang kebal terhadap perubahan jalur pencarian dengan skema eksplisit yang memenuhi syarat semua referensi objek, memastikan perilaku yang konsisten di seluruh sesi pengguna yang berbeda.

```
-- 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;
    $$;
```

Terapkan validasi input dalam fungsi masking untuk menangani kasus tepi dan mencegah perilaku yang tidak terduga. Selalu sertakan penanganan NULL dan validasi format input untuk memastikan perilaku masking yang konsisten. 

```
-- 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;
```

## DMLMemicu perilaku dengan pg\$1columnmask
<a name="AuroraPostgreSQL.Security.DynamicMasking.BestPractices.DMLTriggerBehavior"></a>

Untuk pemicu tabel, tabel transisi akan sepenuhnya dibuka kedoknya. Untuk pemicu tampilan (IOT), tabel transisi akan disamarkan sesuai dengan izin tampilan pengguna saat ini.

Pemicu tabel dengan pg\$1columnmask  
Pemicu dilewatkan tabel transisi yang berisi versi lama dan baru dari baris yang dimodifikasi oleh query DMLnya. Tergantung pada kapan pemicu diaktifkan, Aurora PostgreSQL mengisi baris lama dan baru. Misalnya, `BEFORE INSERT` pemicu hanya memiliki versi baru dari baris dan versi lama kosong karena tidak ada versi lama untuk dirujuk.  
`pg_columnmask`tidak menutupi tabel transisi di dalam pemicu pada tabel. Pemicu dapat menggunakan kolom bertopeng di dalam tubuh mereka dan melihat data yang dibuka kedoknya. Pembuat pemicu harus memastikan bagaimana pemicu dieksekusi untuk pengguna. Contoh berikut berfungsi dengan benar dalam kasus ini.  

```
-- 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;
```
Pembuat pemicu membocorkan data yang dibuka kedok ke pengguna jika mereka tidak berhati-hati dengan pernyataan yang mereka gunakan di badan pemicu mereka. Misalnya menggunakan `RAISE NOTICE ‘%’, masked_column;` cetakan kolom untuk pengguna saat ini.  

```
-- 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
```

Pemicu tampilan dengan pg\$1columnmask (Alih-alih pemicu)  
Pemicu hanya dapat dibuat pada tampilan di PostgreSQL. Mereka digunakan untuk menjalankan pernyataan DHTML pada tampilan yang tidak dapat diperbarui. Tabel transit selalu tertutup di dalam bukan pemicu (IOT), karena tampilan dan tabel dasar yang digunakan di dalam kueri tampilan dapat memiliki pemilik yang berbeda. Dalam hal ini, tabel dasar mungkin memiliki beberapa kebijakan masking yang berlaku pada pemilik tampilan dan pemilik tampilan harus selalu melihat data bertopeng dari tabel dasar di dalam pemicunya. Ini berbeda dari pemicu pada tabel karena dalam hal ini pembuat pemicu dan data di dalam tabel dimiliki oleh pengguna yang sama yang tidak terjadi di sini.  

```
-- 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;
```

Database/Tingkat pengguna GuCs untuk mengontrol perilaku pemicu  
Dua parameter konfigurasi mengontrol perilaku eksekusi pemicu bagi pengguna dengan kebijakan masking yang berlaku. Gunakan parameter ini untuk mencegah pemicu mengeksekusi pada tabel atau tampilan bertopeng saat pembatasan keamanan tambahan diperlukan. Kedua parameter dinonaktifkan secara default, memungkinkan pemicu untuk dijalankan secara normal.  
**GUC Pertama: Memicu pembatasan penembakan pada tabel bertopeng**  
Spesifikasi:  
+ Nama: `pgcolumnmask.restrict_dml_triggers_for_masked_users`
+ Tipe: `boolean`
+ Default: `false` (pemicu diizinkan untuk dieksekusi)
Mencegah eksekusi pemicu pada tabel bertopeng untuk pengguna bertopeng saat disetel ke TRUE. `pg_columnmask`berjalan melalui kesalahan.  
**GUC Kedua: Memicu pembatasan penembakan pada tampilan dengan tabel bertopeng**  
Spesifikasi:  
+ Nama: `pgcolumnmask.restrict_iot_triggers_for_masked_users`
+ Tipe: `boolean`
+ Default: `false` (pemicu diizinkan untuk dieksekusi)
Mencegah eksekusi pemicu pada tampilan yang menyertakan tabel bertopeng dalam definisinya untuk pengguna bertopeng saat disetel ke TRUE.

Parameter ini beroperasi secara independen dan dapat dikonfigurasi seperti parameter konfigurasi basis data standar.

# Skenario pergerakan data Aurora PostgreSQL pg\$1columnmask
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement"></a>

`pg_columnmask`perilaku bervariasi di seluruh operasi pergerakan data yang berbeda tergantung pada apakah operasi terjadi pada lapisan penyimpanan, logis, atau aplikasi. Operasi tingkat penyimpanan (seperti kloning) berperilaku berbeda dari operasi logis (seperti`pg_dump`) dan operasi tingkat aplikasi (seperti kueri FDW). Bagian ini menjelaskan perilaku penyembunyian untuk skenario umum termasuk replikasi, pencadangan, ekspor, dan migrasi, dan menjelaskan implikasi keamanan untuk masing-masing skenario.

**Topics**
+ [Database Global Aurora dan Baca Replika](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.RR)
+ [Kloning basis data dan pemulihan snapshot](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Clones)
+ [Replikasi logis](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.LogRep)
+ [Deployment Biru/Hijau](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.BlueGreen)
+ [Aliran nol-ETL dan CDC](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.ZETL)
+ [AWS Database Migration Service](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DMS)
+ [Ekspor data](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DataExport)
+ [Tampilan dan tampilan terwujud](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Views)
+ [Pembuangan dan pemulihan data](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DDR)
+ [Pembungkus data asing](#AuroraPostgreSQL.Security.DynamicMasking.DataMovement.FDQ)

## Database Global Aurora dan Baca Replika
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.RR"></a>

`pg_columnmask`Kebijakan Aurora disimpan dalam tabel sistem database dalam volume cluster. Semua replika mengakses kebijakan yang sama dan mengembalikan hasil yang disamarkan secara konsisten. Untuk penerapan Aurora Global Database, `pg_columnmask` kebijakan mereplikasi ke sekunder Wilayah AWS bersama dengan tabel sistem database lainnya, memastikan perlindungan data yang konsisten di seluruh wilayah. Selama skenario failover, semua `pg_columnmask` kebijakan tetap utuh dan fungsional.

## Kloning basis data dan pemulihan snapshot
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Clones"></a>

Operasi Aurora Fast Clone dan snapshot restore mempertahankan semua `pg_columnmask` kebijakan, peran, dan konfigurasi sebagai bagian dari tabel sistem database. Database yang dikloning atau dipulihkan mewarisi semua kebijakan yang ada dari kluster sumber. Setelah kloning atau restorasi, setiap cluster database mempertahankan `pg_columnmask` kebijakan independen.

## Replikasi logis
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.LogRep"></a>

Selama sinkronisasi awal, replikasi logis menggunakan operasi SQL COPY standar, dan `pg_columnmask` kebijakan diberlakukan berdasarkan izin pengguna replikasi. Selama CDC (perubahan pengambilan data) yang sedang berlangsung, kebijakan masking tidak diterapkan dan data yang dibuka kedoknya direplikasi melalui catatan WAL. Pengguna dengan `pg_create_subscription` hak istimewa berpotensi mengeksfiltrasi data yang dibuka kedok dengan menyiapkan replikasi ke sistem yang mereka kendalikan.

## Deployment Biru/Hijau
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.BlueGreen"></a>

Selama restorasi snapshot, `pg_columnmask` kebijakan secara otomatis disertakan. Lingkungan hijau dimulai dengan salinan identik dari semua kebijakan dari lingkungan biru. Selama replikasi dari biru ke hijau, data tidak disamarkan. Perubahan kebijakan masking berikutnya (perintah DDL) pada cluster biru tidak bereplikasi ke cluster hijau dan membatalkan penerapan RDS. blue/green 

## Aliran nol-ETL dan CDC
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.ZETL"></a>

Replikasi data tidak terpengaruh oleh `pg_columnmask` kebijakan. Zero-ETL mendukung replikasi DDL tetapi tidak mereplikasi atau kebijakan RLS. `pg_columnmask` Tidak ada kebijakan masking yang diterapkan pada data yang direplikasi di Zero-ETL.

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

Sinkronisasi data awal disamarkan atau dibuka kedok berdasarkan pengguna yang dipilih untuk tugas DMS. Data CDC selalu dibuka kedoknya. Meskipun kebijakan RLS internal `pg_columnmask` terkait dapat dimigrasikan, kebijakan tersebut tidak akan berfungsi pada target non-pg\$1columnmask-enabled.

## Ekspor data
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DataExport"></a>

`pg_columnmask`memperlakukan ekspor seperti operasi kueri lainnya—masking diterapkan berdasarkan izin pengguna yang mengeksekusi. Ini berlaku untuk perintah SQL seperti COPY, SELECT INTO, CREATE TABLE AS, dan fungsi ekspor S3 Aurora PostgreSQL. 

**catatan**  
Saat pengguna bertopeng mengekspor data, file yang dihasilkan berisi nilai bertopeng yang dapat melanggar batasan basis data saat dipulihkan.

## Tampilan dan tampilan terwujud
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.Views"></a>

Ingatlah pertimbangan berikut saat menggunakan tampilan:
+ **Tampilan reguler** — Selalu gunakan `INVOKER` semantik. Kebijakan masking pengguna saat ini berlaku saat menanyakan tampilan, terlepas dari siapa yang membuat tampilan.
+ **Tampilan terwujud** — Saat disegarkan, kebijakan penyembunyian pemilik tampilan terwujud berlaku, bukan kebijakan pengguna yang melakukan penyegaran. Jika pemilik memiliki kebijakan penyembunyian, tampilan terwujud selalu berisi data bertopeng.

## Pembuangan dan pemulihan data
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.DDR"></a>

`pg_dump`beroperasi sebagai pengguna database reguler dan menerapkan kebijakan masking berdasarkan izin pengguna yang menghubungkan. Jika pengguna bertopeng melakukan dump, file cadangan berisi data bertopeng. `pg_columnmask`kebijakan termasuk dalam dump sebagai bagian dari skema database. Restorasi yang berhasil mengharuskan semua peran yang direferensikan ada di database target dan bahwa target memiliki `pg_columnmask` ekstensi yang diinstal.

**catatan**  
Dimulai dengan PostgreSQL 18`pg_dump`, mendukung `—no-policies` opsi yang mengecualikan kebijakan Row Level Security (RLS) dan masking dari dump database. `pg_columnmask` Untuk informasi selengkapnya, lihat [pg\$1dump](https://www.postgresql.org/docs/current/app-pgdump.html).

## Pembungkus data asing
<a name="AuroraPostgreSQL.Security.DynamicMasking.DataMovement.FDQ"></a>

Saat menggunakan pembungkus data asing, kebijakan masking pada tabel jarak jauh diterapkan berdasarkan izin pengguna yang dipetakan di server sumber, bukan izin pengguna kueri lokal, dan meskipun Anda dapat mengakses data jarak jauh bertopeng melalui FDW, Anda tidak dapat membuat kebijakan DDM atau RLS secara langsung pada tabel asing di database lokal Anda.