All Products
Search
Document Center

MaxCompute:Dynamic data masking

Last Updated:Mar 25, 2026

Dynamic data masking lets you control what sensitive data specific users see—without changing their SQL queries or the underlying data. When a user queries a table, MaxCompute intercepts the results and applies masking in real time based on the user's identity and role.

When to use dynamic data masking

Dynamic data masking is useful when different users need different levels of access to the same columns. For example:

  • A customer support agent can confirm the last four digits of a phone number without seeing the full number.

  • A developer can run queries against production data for troubleshooting without exposing personally identifiable information (PII).

  • A data analyst can work with salary ranges rather than exact figures.

Masking applies consistently across queries, downloads, joins, and user-defined function (UDF) computations.

How it compares to other access controls

Control methodHow it worksSQL query changes requiredBest for
Dynamic data maskingMasks column values at query time based on user/roleNoShowing different data views to different users
Access Control List (ACL) column-level permissionsBlocks access to entire columnsYes — users must rewrite queries to exclude restricted columnsPreventing access to sensitive columns entirely
Row-level permissionsFilters rows based on user/roleNoRestricting which rows a user can see
Dynamic data masking and row-level permissions are mutually exclusive for the same table. You cannot apply both to a single table.

How it works

Project owners and users with the Super_Administrator or Admin role manage masking policies. When a user queries a table:

  1. MaxCompute checks which masking policies apply to the user's identity and roles.

  2. For each sensitive column, the applicable policy (or the highest-priority policy if multiple apply) determines what the user sees.

  3. The system returns either masked or plaintext data—without modifying the underlying data.

image

Masking is applied at the point closest to data retrieval from storage, which provides high performance and security.

image

Limitations

Availability: This feature is in public preview and is available in the following regions: China (Hangzhou), China (Shanghai), China (Beijing), China (Zhangjiakou), China (Ulanqab), China (Shenzhen), China (Chengdu), China (Hong Kong), Japan (Tokyo), Singapore, Malaysia (Kuala Lumpur), Indonesia (Jakarta), Germany (Frankfurt), and US (Silicon Valley).

Supported connection methods:

Connection methodMinimum version
Java SDK0.48.0-public
odpscmd0.47.1
JDBC3.4.3
MaxFrameUnlimited
PyODPSUnlimited
Go SDKUnlimited

Tables and views:

  • Masking policies apply to internal tables only, not foreign tables.

  • For traditional views, masking policies are synchronized with the source table. Attaching or detaching a policy from the source table also affects the traditional view.

  • For materialized views, the source table's masking policy is applied at creation time. Subsequent policy changes on the source table do not affect the materialized view.

  • If you mask Chinese characters, the column encoding must be UTF-8.

Enable or disable masking for a project

Only a project owner or a user with the Super_Administrator or Admin role can enable or disable the odps.data.masking.policy.enable project property.

-- Enable dynamic data masking for the project
setproject odps.data.masking.policy.enable=true;

-- Disable dynamic data masking for the project
setproject odps.data.masking.policy.enable=false;

Create a masking policy

A masking policy defines who sees masked data and which masking method to use.

CREATE DATA MASKING POLICY [IF NOT EXISTS] <policy_name>
TO { USER <user_list> | ROLE <role_list> | DEFAULT }
USING <predefined_masking_policy>;

To delete a policy:

DROP DATA MASKING POLICY <policy_name>;

Parameters:

ParameterRequiredDescription
policy_nameYesThe policy name. Case-insensitive. Can contain letters, digits, and underscores (_). Start with a letter. Maximum 128 bytes.
USERYes (pick one)Masks data for specific users. Run list users; to get usernames.
ROLEYes (pick one)Masks data for specific roles. Run list roles; to get role names.
DEFAULTYes (pick one)Fallback policy. Applied when no user- or role-specific policy matches.
predefined_masking_policyYesThe masking method to apply. See Predefined masking policies.

Examples:

-- Apply MD5 hashing to data seen by userA, userB, and userC
CREATE DATA MASKING POLICY IF NOT EXISTS masking_test_001
TO USER (userA, userB, userC)
USING MASKED_MD5(0);

-- Apply MD5 hashing to data seen by the developer and O&M roles
CREATE DATA MASKING POLICY IF NOT EXISTS masking_test_001
TO ROLE (role_project_deploy, role_project_dev)
USING MASKED_MD5(0);

Apply a policy to a column

Bind or unbind masking policies to specific columns of a table.

-- Bind a policy to a column
APPLY DATA MASKING POLICY <policy_name> BIND TO TABLE <table_name> COLUMN <column_name>;

-- Unbind a specific policy from a column
APPLY DATA MASKING POLICY <policy_name> UNBIND FROM TABLE <table_name> COLUMN <column_name>;

-- Unbind all policies from a column
APPLY DATA MASKING POLICY UNBIND ALL FROM TABLE <table_name> COLUMN <column_name>;

-- Unbind all policies from all columns of a table
APPLY DATA MASKING POLICY UNBIND ALL FROM TABLE <table_name>;

Parameters:

ParameterRequiredDescription
policy_nameYesThe name of the masking policy.
table_nameYesThe table containing the sensitive column.
column_nameYesThe sensitive column to mask.

View masking policies

-- View details of a specific policy
DESC DATA MASKING POLICY <policy_name>;

-- View extended table info, including sensitive columns and applied policies
DESC EXTENDED <table_name>;

-- List all policies in the current project
LIST DATA MASKING POLICY;

-- List policies applied to a user
LIST DATA MASKING POLICY TO USER <user_name>;

-- List policies applied to a role
LIST DATA MASKING POLICY TO ROLE <role_name>;

-- List all policies on a table
LIST DATA MASKING POLICY ON <table_name>;

-- List all policies on a specific column
LIST DATA MASKING POLICY ON <table_name> TO COLUMN <column_name>;

Predefined masking policies

Choose a masking method based on your data type and sensitivity requirements.

CategoryPolicyCommandSupported typesDescription
No maskingUNMASKEDUNMASKEDAllReturns data as-is. Use this to explicitly grant plaintext access.
NullifyMASKED_NULLIFYMASKED_NULLIFYAllReturns NULL.
Default valueMASKED_DVMASKED_DVAllReplaces data with the type's default value (0, "", false, epoch timestamps, etc.).
Date truncationMASKED_DATE_YEARMASKED_DATE_YEARDATE, DATETIME, TIMESTAMP_NTZ, TIMESTAMPKeeps only the year; sets month, day, and time to January 1, 00:00 (UTC).
Numeric roundingMASKED_POINT_RESERVEMASKED_POINT_RESERVE(<num>)DECIMAL, FLOAT, DOUBLERounds to num decimal places (0–5).
Mask start and endMASKED_STRING_MASKED_BAMASKED_STRING_MASKED_BA(<before>, <after>)STRING, VARCHAR, CHAR, BINARYReplaces the first before and last after characters with *.
Mask middleMASKED_STRING_UNMASKED_BAMASKED_STRING_UNMASKED_BA(<before>, <after>)STRING, VARCHAR, CHAR, BINARYKeeps the first before and last after characters in plaintext; masks the middle with *.
SHA256 hashingMASKED_SHA256MASKED_SHA256(<salt>)STRING, VARCHAR, CHAR, BINARYHashes data with SHA256. salt is an integer from 0–9.
SHA512 hashingMASKED_SHA512MASKED_SHA512(<salt>)STRING, VARCHAR, CHAR, BINARYHashes data with SHA512. salt is an integer from 0–9.
MD5 hashingMASKED_MD5MASKED_MD5(<salt>)STRING, VARCHAR, CHAR, BINARYHashes data with MD5. salt is an integer from 0–9.
SM3 hashingMASKED_SM3MASKED_SM3(<salt>)STRING, VARCHAR, CHAR, BINARYHashes data with SM3. salt is an integer from 0–9.
Random replacementMASKED_REPLACE_RANDOMMASKED_REPLACE_RANDOM(<position>)STRING, VARCHAR, CHAR, BINARYReplaces characters with random digits and letters. position=0: all; position>0: first N; position<0: last N.
Random replacement at start and endMASKED_REPLACE_RANDOM_BAMASKED_REPLACE_RANDOM_BA(<before>, <after>)STRING, VARCHAR, CHAR, BINARYReplaces the first before and last after characters with random digits and letters.
Fixed replacementMASKED_REPLACE_FIXEDMASKED_REPLACE_FIXED(<position>, <fixed_string>)STRING, VARCHAR, CHAR, BINARYReplaces characters with a fixed string (≤100 chars, no spaces). position=0: all; position>0: first N; position<0: last N.

Policy examples

MASKED_NULLIFY — set to null:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking
| col_string |
| NULL       |

MASKED_DV — assign default value:

-- Input (timestamp), project time zone: UTC+8
| col_timestamp       |
| 2024-05-01 11:12:13 |

-- After masking
| col_timestamp       |
| 1970-01-01 08:00:00 |

MASKED_DATE_YEAR — keep only year:

-- Input (timestamp), project time zone: UTC+8
| col_timestamp       |
| 2024-05-01 11:12:13 |

-- After masking
| col_timestamp       |
| 2024-01-01 08:00:00 |

MASKED_POINT_RESERVE(2) — round to 2 decimal places:

-- Input (float)
| col_float |
| 1.12345   |

-- After masking
| col_float |
| 1.12      |

MASKED_STRING_MASKED_BA(3, 3) — mask start and end:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking: first 3 and last 3 characters replaced with *
| col_string      |
| ***hael John*** |

MASKED_STRING_UNMASKED_BA(1, 1) — mask middle:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking: only first and last characters kept in plaintext
| col_string      |
| M*************n |

MASKED_SHA256(0) — SHA256 hash:

-- Input (string)
| col_string          |
| 4562-1234-5678-9123 |

-- After masking
| col_string                               |
| zwGMB1aCF1t705EfcwdDorql4MZb46XBqQJw/2RVx8U= |

MASKED_SHA512(0) — SHA512 hash:

-- Input (string)
| col_string          |
| 4562-1234-5678-9123 |

-- After masking
| col_string                                                                 |
| 3PPywfEIp08WuTUI8FZCCfdVuRu68wZTVwWWVAf4pboACUnH6w9kFMLpl2AARaGW/mvWvg26p0EIqmE0fAEiuA== |

MASKED_MD5(0) — MD5 hash:

-- Input (string)
| col_string          |
| 4562-1234-5678-9123 |

-- After masking
| col_string               |
| mK/o08tew5g7S3XV/BkFfw== |

MASKED_SM3(0) — SM3 hash:

-- Input (string)
| col_string          |
| 4562-1234-5678-9123 |

-- After masking
| col_string                               |
| Q2TfwUh4B8QQH8jPL6DfdoGysx/CXBxn2T14dDwQtQw= |

MASKED_REPLACE_RANDOM(7) — replace first 7 characters randomly:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking
| col_string      |
| 4DlJQxi Johnson |

MASKED_REPLACE_RANDOM_BA(4, 4) — replace start and end randomly:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking: first 4 and last 4 characters replaced randomly
| col_string      |
| r0xEael JohnWNr |

MASKED_REPLACE_FIXED(7, "Oli") — replace first 7 characters with fixed string:

-- Input (string)
| col_string      |
| Michael Johnson |

-- After masking
| col_string  |
| Oli Johnson |

Policy priority

When a user matches multiple masking policies on the same column, the policy with the highest priority (lowest priority number) takes effect.

For example, if a user matches both MASKED_REPLACE_RANDOM(3) (priority 3) and MASKED_SM3 (priority 4), MASKED_REPLACE_RANDOM(3) applies.

PriorityPolicy
0 (highest)UNMASKED
1MASKED_POINT_RESERVE
2MASKED_DATE_YEAR
3MASKED_STRING_MASKED_BA, MASKED_STRING_UNMASKED_BA, MASKED_REPLACE_RANDOM, MASKED_REPLACE_RANDOM_BA, MASKED_REPLACE_FIXED
4MASKED_SHA256, MASKED_SHA512, MASKED_MD5, MASKED_SM3
5MASKED_DV
6 (lowest)MASKED_NULLIFY

Examples

Mask personal information columns

This example masks several columns in a personal information table for a specific user, while hashing phone numbers for all other users by default.

Step 1: Prepare the table and data.

-- Create the table
CREATE TABLE IF NOT EXISTS personal_info (
  id bigint COMMENT 'The unique ID of the user.',
  name string COMMENT 'The name of the user.',
  age int COMMENT 'The age of the user.',
  gender string COMMENT 'The gender of the user.',
  height float COMMENT 'The height of the user.',
  birthday date COMMENT 'The birth date of the user.',
  phone_number string COMMENT 'The phone number of the user.',
  email string COMMENT 'The email address of the user.',
  address string COMMENT 'The address of the user.',
  salary decimal(18, 2) COMMENT 'The salary of the user.',
  create_time timestamp COMMENT 'The time when the user information was created.',
  update_time timestamp COMMENT 'The time when the user information was updated.',
  is_deleted boolean COMMENT 'A flag that indicates whether the user information is deleted.'
);

-- Insert sample data
INSERT INTO personal_info VALUES
    (1, 'Zhang San', 18, 'Male', 178.56, '1990-01-01', '13800000000', 'zhangsan@example.com', 'Haidian District, Beijing', 5000.00, '2023-04-19 11:32:00', '2023-04-19 11:32:00', false),
    (2, 'Li Si', 20, 'Female', 162.70, '1992-02-02', '13900000000', 'lisi@example.com', 'Pudong New Area, Shanghai', 6000.00, '2023-04-19 11:32:00', '2023-04-19 11:32:00', false),
    (3, 'Wang Wu', 22, 'Male', 185.21, '1994-03-03', '14000000000', 'wangwu@example.com', 'Nanshan District, Shenzhen', 7000.00, '2023-04-19 11:32:00', '2023-04-19 11:32:00', false);

Step 2: Create and apply masking policies.

-- name: keep only the first character, mask the rest with *
CREATE DATA MASKING POLICY IF NOT EXISTS masking_name
TO USER (RAM$xxx@test.aliyunid.com:xxx)
USING MASKED_STRING_UNMASKED_BA(1, 0);

APPLY DATA MASKING POLICY masking_name BIND TO
TABLE personal_info COLUMN name;

-- height: round to 0 decimal places
CREATE DATA MASKING POLICY IF NOT EXISTS masking_height
TO USER (RAM$xxx@test.aliyunid.com:xxx)
USING MASKED_POINT_RESERVE(0);

APPLY DATA MASKING POLICY masking_height BIND TO
TABLE personal_info COLUMN height;

-- birthday: keep only the year
CREATE DATA MASKING POLICY IF NOT EXISTS masking_birthday
TO USER (RAM$xxx@test.aliyunid.com:xxx)
USING MASKED_DATE_YEAR;

APPLY DATA MASKING POLICY masking_birthday BIND TO
TABLE personal_info COLUMN birthday;

-- phone_number: SM3 hash for all other users by default
CREATE DATA MASKING POLICY default_sm3
TO DEFAULT
USING MASKED_SM3(0);

APPLY DATA MASKING POLICY default_sm3 BIND TO
TABLE personal_info COLUMN phone_number;

Step 3: Query as the masked user.

SELECT id, name, height, birthday, phone_number FROM personal_info;

Before masking:

+----+-----------+--------+------------+--------------+
| id | name      | height | birthday   | phone_number |
+----+-----------+--------+------------+--------------+
| 1  | Zhang San | 178.56 | 1990-01-01 | 13800000000  |
| 2  | Li Si     | 162.7  | 1992-02-02 | 13900000000  |
| 3  | Wang Wu   | 185.21 | 1994-03-03 | 14000000000  |
+----+-----------+--------+------------+--------------+

After masking:

+----+--------+--------+------------+----------------------------------------------+
| id | name   | height | birthday   | phone_number                                 |
+----+--------+--------+------------+----------------------------------------------+
| 1  | Zhang* | 179.0  | 1990-01-01 | lvYJaH4ElL2ilpQx/8tfMUw7xP22yblIgmfWp0/msUQ= |
| 2  | Li*    | 163.0  | 1992-01-01 | 9fFWacNSwCRZLAjMHqunlfwkqhTbP2ubuDOeOSh4N1c= |
| 3  | Wang*  | 185.0  | 1994-01-01 | k/0JoQCSarJg9ATJ5tyVnhQf1jIBxHXRbB+cvUm4OmE= |
+----+--------+--------+------------+----------------------------------------------+

Grant plaintext access to privileged users

Use UNMASKED to let specific users bypass masking while all other users see hashed data.

-- All users see SHA256-hashed data by default
CREATE DATA MASKING POLICY default_hash_policy
TO DEFAULT
USING MASKED_SHA256(5);

-- Users A and B see plaintext data
CREATE DATA MASKING POLICY ab_unmask_policy
TO USER (A, B)
USING UNMASKED;

Users A and B match both policies. Because UNMASKED has priority 0 (the highest), they see plaintext. All other users see SHA256-hashed data.

When a user matches multiple policies on the same column, the policy with the highest priority takes effect. For the full priority order, see Policy priority.

Appendix: Default values for MASKED_DV

Data typeDefault value
bigint0
double0.0
decimal0
string""
datetimeDATETIME'1970-01-01 00:00:00' (UTC)
booleanfalse
tinyint0
smallint0
int0
binary''
float0.0
varchar(n)""
char(n)" " (n spaces)
dateDATE'1970-01-01'
timestampTIMESTAMP'1970-01-01 00:00:00' (UTC)
timestamp_ntzTIMESTAMP'1970-01-01 00:00:00' (UTC)
array{Default value of the child class}
map{key:value} (key and value correspond to the default values of the child class)
json""
struct(Default value of the child class)