Padrões comuns para filtragem de linhas e mascaramento de colunas
Esta página descreve padrões comuns para implementar políticas ABAC de filtro de linha e máscara de coluna.
- Para conceitos gerais, consulte Conceitos fundamentais para controle de acesso baseado em atributos (ABAC).
- Para a sintaxe da política, consulte Criar e gerenciar políticas de filtro de linha e máscara de coluna.
- Para políticas GRANT, consulte políticas ABAC GRANT.
- Se o seu ambiente usar RBAC, consulte Usar RBAC com ABAC para saber como as funções de identidade se comportam quando os usuários assumem uma função e os padrões que combinam RBAC com ABAC.
Funções de mascaramento compatíveis com Cast
O Databricks converte automaticamente a saída da função de mascaramento para corresponder ao tipo de dados da coluna de destino. Consulte Conversão automática de tipos para máscaras de coluna.
Os seguintes padrões ajudam você a projetar funções de mascaramento compatíveis com a conversão de tipo.
Retorna um tipo que pode ser convertido para outro tipo.
Ao mascarar uma coluna, retorne o mesmo tipo de dados ou um tipo que possa ser convertido para ele. Verifique os tipos de dados das colunas que sua política visa e certifique-se de que cada ramo da função retorne um valor compatível.
-- Succeeds: Masks a DOUBLE column, returns DOUBLE in every branch
CREATE FUNCTION mask_salary(salary DOUBLE, user_role STRING)
RETURNS DOUBLE
RETURN CASE
WHEN user_role IN ('admin', 'hr') THEN salary
WHEN user_role = 'manager' THEN ROUND(salary / 1000) * 1000
ELSE 0.0
END;
-- Fails: 'CONFIDENTIAL' cannot be cast to a DOUBLE column type
CREATE FUNCTION mask_salary_as_text(salary DOUBLE, user_role STRING)
RETURNS STRING
RETURN CASE
WHEN user_role IN ('admin', 'hr') THEN CAST(salary AS STRING)
ELSE 'CONFIDENTIAL'
END;
Evite estouro numérico
Quando uma função de máscara aceita e retorna um tipo numérico mais amplo do que a coluna de destino, o resultado é automaticamente convertido de volta para o tipo da coluna. Se o valor retornado exceder o intervalo do tipo mais restrito, ocorrerá um estouro de conversão e a consulta falhará em tempo de execução.
-- The target column is TINYINT (max 127). The input is upcast to BIGINT
-- for the function. Adding 1000 produces a BIGINT result that overflows
-- when cast back to TINYINT.
CREATE FUNCTION mask_score(score BIGINT)
RETURNS BIGINT
RETURN score + 1000;
Use VARIANT para vários tipos de coluna.
Consulte Como mascarar vários tipos de coluna com uma única função.
Compatibilidade de elenco de teste
Teste funções de mascaramento com diferentes padrões de dados.
SELECT CAST(mask_salary(salary, 'admin') AS DOUBLE) FROM employees;
SELECT CAST(mask_salary(salary, 'manager') AS DOUBLE) FROM employees;
SELECT CAST(mask_salary(salary, 'viewer') AS DOUBLE) FROM employees;
Como mascarar vários tipos de coluna com uma única função
Uma única UDF de mascaramento que aceita e retorna um VARIANT pode mascarar colunas de vários tipos de dados, o que reduz o número de UDFs e políticas que você precisa manter. O Databricks converte o valor da coluna em VARIANT antes de a função entrar em execução e, em seguida, converte o valor de retorno de volta para o tipo da coluna seguindo as regras do SQL ANSI.
Dentro da função, use schema_of_variant() para inspecionar o valor e fazer branch em seu tipo. Em cada branch, retorne um valor que possa ser convertido para o tipo da coluna de destino.
Mascarar vários tipos numéricos
A máscara VARIANT mais simples retorna uma única constante que o Databricks converte para o tipo de cada coluna. Uma política que usa essa função pode mascarar as colunas INT, DOUBLE e DECIMAL, sem uma função separada para cada precisão:
CREATE FUNCTION mask_numeric(val VARIANT)
RETURNS VARIANT
DETERMINISTIC
RETURN 0::VARIANT;
Para variar o valor mascarado por tipo, Branch em schema_of_variant() e retorne um valor apropriado para cada um:
CREATE FUNCTION flexible_mask(data VARIANT)
RETURNS VARIANT
RETURN CASE
WHEN schema_of_variant(data) = 'BIGINT' THEN 0::VARIANT
WHEN schema_of_variant(data) = 'DATE' THEN DATE'1970-01-01'::VARIANT
WHEN schema_of_variant(data) = 'DOUBLE' THEN 0.00::VARIANT
ELSE NULL::VARIANT
END;
Integer types são ampliados para BIGINT em um VARIANT, portanto, faça branch em BIGINT em vez de INT ou TINYINT.
Mascarar colunas STRUCT, ARRAY e MAP
A abordagem VARIANT também mascara colunas de tipos complexos, estendendo o exemplo numérico acima aos dados aninhados. Uma coluna STRUCT, ARRAY ou MAP chega à função como VARIANT, e o Databricks converte o que a função retornar de volta para o tipo declarado da coluna. O valor retornado deve ter o mesmo esquema da entrada; caso contrário, a conversão falha e a query gera um erro.
STRUCT As colunas têm suporte no Databricks Runtime 18.1 e acima. As colunas ARRAY e MAP têm suporte no Databricks Runtime 19 e acima. Essa conversão funciona apenas em políticas de máscara de coluna e filtro de linha ABAC, e não em máscaras gerais de SQL ou em nível de tabela.
A seguinte função de exemplo mascara tipos STRUCT, ARRAY e MAP específicos. Adicione um branch WHEN para cada esquema que você precisa mascarar:
CREATE OR REPLACE FUNCTION generic_mask(val VARIANT)
RETURNS VARIANT
RETURN CASE
-- STRUCT<id: INT, ssn: STRING>: keep id, redact ssn
WHEN schema_of_variant(val) = 'OBJECT<id: BIGINT, ssn: STRING>' THEN
to_variant_object(named_struct('id', val:id, 'ssn', 'xxx-xx-xxxx'))
-- ARRAY<STRING>: return a single redacted element
WHEN schema_of_variant(val) = 'ARRAY<STRING>' THEN
to_variant_object(array('redacted'))
-- MAP<STRING, STRING>: redact every value
WHEN schema_of_variant(val) = 'OBJECT<key1: STRING, key2: STRING>' THEN
to_variant_object(map('key1', 'redacted', 'key2', 'redacted'))
ELSE NULL::VARIANT
END;
Cada branch WHEN faz duas coisas, descritas nas seções a seguir:
- Identificar o esquema VARIANT. Cada esquema
VARIANTprecisa de sua própria lógica de mascaramento, portanto, a função Branch na string de esquema queschema_of_variant(val)retorna para aplicar a remoção correta de dados a cada um. - Reconstruir o valor mascarado. Construa um valor redação do tipo da coluna e encapsule-o em
to_variant_object()para retornar umVARIANT.
Um valor cujo esquema não corresponde a nenhum branch é direcionado para ELSE.
Identifique o esquema VARIANT
Faça a correspondência com o esquema VARIANT do valor, e não com o tipo declarado da coluna: a conversão para VARIANT normaliza os dados, de modo que o esquema pode diferir da definição da coluna. Os exemplos a seguir mostram casos comuns:
Tipo da coluna |
| Notas |
|---|---|---|
|
| |
|
| integer types são ampliados para |
|
|
|
|
| Os campos são classificados em ordem alfabética por key, e não pela ordem de declaração. |
|
| |
|
| Varia por linha: as keys de cada linha determinam o esquema. |
|
| Uma linha diferente da mesma coluna produz um esquema diferente. |
|
| Um valor nulo torna-se |
|
|
Reconstruir o valor mascarado
Para cada esquema que você corresponder, crie um valor redigido com a mesma estrutura e, em seguida, envolva-o em to_variant_object():
- Use
named_struct()para reconstruir umSTRUCT, mantendo os campos desejados e substituindo o resto. - Use
array()para reconstruir umARRAY. - Use
map()para recompilar umMAPcom valores redigidos.
O Databricks converte o VARIANT retornado de volta para o tipo declarado da coluna, portanto, cada valor recompilado deve poder ser convertido para esse tipo.
Testar uma máscara VARIANT
Antes de anexar uma função a uma política, você pode testá-la em uma query simples para confirmar a saída mascarada. A função a seguir mascara uma coluna ARRAY<STRUCT<id: BIGINT, value: FLOAT>> e gera um erro para qualquer outro esquema:
CREATE OR REPLACE FUNCTION mask_points(v VARIANT)
RETURNS VARIANT
RETURN CASE
WHEN schema_of_variant(v) = 'ARRAY<OBJECT<id: BIGINT, value: FLOAT>>' THEN
to_variant_object(array(named_struct('id', 1, 'value', 2.1)))
ELSE raise_error('Unexpected VARIANT schema: ' || schema_of_variant(v))
END;
Converta a coluna com to_variant_object(), aplique a função de mascaramento e use variant_get() para converter novamente o valor mascarado de VARIANT para o tipo da coluna. Isso reflete o que a política faz em tempo de execução (runtime):
SELECT variant_get(mask_points(to_variant_object(points)), '$', typeof(points)) AS masked
FROM my_catalog.my_schema.my_table;
Limitações
- Uma coluna complexa que contém
CHAR,VARCHAR,GEOMETRY,GEOGRAPHYouTIMEnão pode ser mascarada comVARIANT. MAPas keys devem serSTRING. As colunas digitadasMAP<INT, ...>,MAP<DATE, ...>e assim por diante não são convertidas.
Impeça o acesso até que as colunas sensíveis sejam marcadas.
Um padrão comum de governança é controlar o acesso com base na classificação dos dados. Você pode implementar isso com uma tag restritiva default e políticas que aplicam diferentes níveis de proteção dependendo do status de classificação.
- Aplique uma tag como
classification : unverifieda todos os novos objetos por default, por meio de automação ou por meio de herança tag , aplicando a tag no nível do catálogo ou esquema, para que quaisquer novas tabelas adicionadas ao catálogo ou esquema herdem automaticamente a tag. - Crie uma política de filtro de linha que bloqueie o acesso às tags de tabela
classification : unverified. - Crie uma política de máscara de coluna que oculte colunas sensíveis em tabelas onde a tag
classification : unverifiednão está mais presente. - Quando uma gestão de dados conclui a classificação, ela atualiza a tag. A política de bloqueio deixa de ser válida e a política de mascaramento entra em vigor.
-- Block access to unverified tables for all non-admin users
CREATE FUNCTION catalog.schema.block_all() RETURNS BOOLEAN
RETURN FALSE;
CREATE POLICY block_unverified
ON CATALOG my_catalog
ROW FILTER catalog.schema.block_all
TO `account users` EXCEPT `data_admins`
FOR TABLES
WHEN has_tag_value('classification', 'unverified');
Para proteger dados sensíveis após sua classificação, defina uma política de máscara de coluna que entre em vigor quando a tag classification : unverified não estiver mais presente:
CREATE FUNCTION catalog.schema.mask_pii(val STRING)
RETURNS STRING
RETURN '***';
CREATE POLICY mask_reviewed_pii
ON CATALOG my_catalog
COLUMN MASK catalog.schema.mask_pii
TO `account users`
EXCEPT `data_admins`
FOR TABLES
WHEN NOT has_tag_value('classification', 'unverified')
MATCH COLUMNS (has_tag_value('pii', 'name') OR has_tag_value('pii', 'address')) AS m
ON COLUMN m;
Revelação parcial sem expressão regular
Revele parte de um valor sensível usando operações com strings em vez de expressões regulares. A máscara baseada em expressões regulares examina o valor inteiro de cada linha, o que é dispendioso em campos de texto grandes (consulte Evitar a máscara de expressões regulares em campos de texto grandes).
CREATE FUNCTION mask_ssn(ssn STRING, show_last INT) RETURNS STRING
DETERMINISTIC
RETURN CONCAT('***-**-', RIGHT(ssn, show_last));
Hashing consistente (pseudonimização determinística)
A criptografia consistente (também chamada de pseudonimização determinística) substitui dados sensíveis por um valor criptografado que é o mesmo em várias tabelas. Marcar uma função como DETERMINISTIC informa ao mecanismo que a função sempre retorna o mesmo resultado para a mesma entrada, o que ajuda a otimizar a consulta. Consulte Usar expressões determinísticas e à prova de erros.
A função a seguir realiza um hash consistente do valor de uma string e usa um parâmetro version para suportar a rotação key . Incremente o número version através da cláusula USING COLUMNS da política para gerar novos hashes sem quebrar os dados históricos que usaram a versão anterior. A função concatena o valor original com o número da versão antes de gerar o hash, portanto, a mesma entrada com a mesma versão sempre produz o mesmo hash.
CREATE FUNCTION pseudonymize(val STRING, version INT) RETURNS STRING
DETERMINISTIC
RETURN SHA2(CONCAT(val, CAST(version AS STRING)), 256);
Mascarar uma coluna com base nos atributos do usuário que realiza a consulta
Beta
Os atributos de identidade nas políticas ABAC estão em Beta. Para usá-los, um administrador de account deve:
- Habilite a prévia Identity Attributes in ABAC Policies na página Previews do console da account. Consulte Gerenciar prévias em nível de conta.
- Configure o provisionamento de atributos de identidade para a account. Consulte Atributos de identidade.
Uma política de máscara de coluna pode usar os atributos de identidade do usuário que realiza a consulta para mascarar dados confidenciais sem a necessidade de grupos dedicados. Por exemplo, é possível manter os dados desmascarados para usuários com department = HR e mascará-los para todos os outros.
Esses padrões exigem atributos de identidade provisionados aos seus usuários a partir do seu provedor de identidade, e as funções se comportam de maneira diferente das condições apenas de tag, de formas que afetam como você escreve a política. Antes de usá-los, revise Funções de atributo de identidade e atributos de identidade.
As funções são resolvidas para false quando o usuário não tem valor para o atributo ou quando a key de atributo não existe. Escreva a condição de modo que esse resultado false restrinja o acesso em vez de concedê-lo. Negue a correspondência com NOT para que a máscara seja aplicada, a menos que o atributo corresponda. Por exemplo, WHEN NOT has_identity_attribute_value('department', 'HR') mascara a coluna para todos, exceto para usuários cujo departamento é HR, e como um valor ausente também é false, usuários sem atributo de departamento também são mascarados. Evite o inverso: uma condição que mascara apenas quando o atributo corresponde deixa os usuários que não têm valor para o atributo sem máscara.
Para o comportamento de avaliação, consulte Condições de atributos de identidade. Para limitações, consulte Atributos de identidade em condições de política.
Corresponder a um valor fixo
Mascarar ssn para todos cujo departamento não seja HR:
CREATE FUNCTION hr_catalog.people.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';
CREATE OR REPLACE POLICY mask_ssn_non_hr
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_identity_attribute_value('department', 'HR')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;
Neste exemplo, um usuário cujo departamento é HR vê valores reais. Um usuário em qualquer outro departamento, e um usuário sem atributo de departamento, ambos veem a máscara.
Corresponder a uma tag controlada
O exemplo anterior nomeia um valor de atributo específico (HR) na política, portanto, cobrir vários departamentos significaria escrever uma política separada para cada um. Para cobrir todos os departamentos com uma única política, marque cada tabela com o departamento que a possui e, em seguida, compare o atributo department do usuário que realiza a consulta com essa tag. A coluna é revelada apenas quando o departamento do usuário corresponde ao valor dept_tag da tabela:
CREATE FUNCTION prod.sales.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';
CREATE OR REPLACE POLICY mask_unless_dept_matches
ON SCHEMA prod.sales
COLUMN MASK prod.sales.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_identity_attribute_tag_match('department', 'dept_tag')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;
As chaves e os valores de atributo diferenciam maiúsculas de minúsculas, e os valores são comparados exatamente: Finance e finance não correspondem.
Restringir o acesso de agentes externos que atuam em nome de um usuário
Beta
Os atributos de contexto em políticas ABAC estão em Beta. Para usá-los, um administrador da conta deve habilitar a pré-visualização UC ABAC Context Attributes na página Previews do console da conta. Consulte Gerenciar prévias em nível de account.
Context attributes can be used to restrict data access for requests made on a user's behalf through an OAuth application (user-to-machine (U2M) authorization). If agents are connected via OAuth, this setup can be used to prevent them from accessing data when they act on behalf of a user, even though the user can still read the data when they query it directly in the workspace.
Qualquer acesso autenticado por OAuth por meio da CLI do Databricks, dos SDKs ou da API de Execução de Instrução SQL define request.is_on_behalf_of como 'true', mesmo quando um usuário está realizando consultas manualmente. O acesso autenticado com um access token pessoal (PAT) não faz isso. O acesso ao Genie não pode ser capturado por meio deste mecanismo.
Esses padrões usam as funções de atributo de contexto. Para atributos e comportamento disponíveis, consulte Funções de atributo de contexto (Beta).
Conectar o agente ao Databricks
Para usar atributos de contexto, conecte o agente usando um aplicativo OAuth personalizado:
- Um administrador da account habilita a pré-visualização UC ABAC Context Attributes no console da account. Consulte Gerenciar prévias do Databricks.
- Um administrador da account registra um aplicativo OAuth personalizado no console da account e anota seu ID do cliente.
- Conecte o agente ao MCP gerenciado pelo Databricks por meio desse aplicativo OAuth. Consulte Configurar um cliente OAuth personalizado.
Um agente que usa o cliente databricks-cli integrado ainda se autentica via OAuth, portanto, request.is_on_behalf_of lê 'true'. No entanto, você não pode distinguir suas solicitações do uso manual da CLI, porque ambos compartilham o ID de cliente databricks-cli. Para governar um aplicativo específico, faça o registro de um aplicativo OAuth personalizado e conecte o agente por meio dele.
Certifique-se de que um agente não consiga acessar os dados por meio de um caminho que sua política não cobre:
- Se você restringir o acesso com base em
request.is_on_behalf_of, certifique-se de que o agente não possa se autenticar com um PAT. Um PAT não definerequest.is_on_behalf_ofcomo'true', portanto, uma condição nesse atributo não o restringe. - Se você restringir o acesso com base em
request.client_id, certifique-se de que o agente não possa se conectar por meio de um cliente que sua condição não cubra, como o clientedatabricks-cligenérico.
Mascarar uma coluna para solicitações em nome de terceiros
Mascare ssn para solicitações executadas em nome de um usuário, como um agente agindo por meio de um aplicativo OAuth registrado, enquanto o deixa sem máscara para query diretas:
CREATE FUNCTION hr_catalog.people.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';
CREATE OR REPLACE POLICY mask_ssn_for_agents
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN has_context_attribute_value('request.is_on_behalf_of', 'true')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;
Neste exemplo, uma query direta retorna valores reais, e uma solicitação em nome de outro vê os valores mascarados. Usando a CLI e a API de Execução de Instrução SQL, request.is_on_behalf_of também lê 'true', portanto, esta política mascara a coluna para essas solicitações também. Para direcionar a um aplicativo específico, faça a correspondência de request.client_id com o ID do cliente desse aplicativo.
Restringir uma coluna a um aplicativo aprovado
Mascare ssn para cada solicitação externa, exceto aquelas do seu aplicativo aprovado, identificado pelo seu ID de cliente OAuth:
CREATE OR REPLACE POLICY mask_ssn_unapproved_apps
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_context_attribute_value('request.client_id', '<your-app-client-id>')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;
Para ver qual aplicativo fez uma solicitação, inspecione o campo identity_metadata.acting_resource nos logs de auditoria.
Filtragem de linhas com predicados somente de coluna
Filtrar linhas usando lógica booleana simples que referencia apenas colunas da tabela. Os predicados somente de coluna permitem o pushdown de predicados, o que permite que o mecanismo ignore dados irrelevantes durante as varreduras (consulte Entender o pushdown de predicados em tabelas protegidas).
CREATE FUNCTION filter_by_region(region STRING, allowed STRING)
RETURNS BOOLEAN
DETERMINISTIC
RETURN array_contains(split(allowed, ','), lower(region));
Utilize com uma política que passe as regiões permitidas como uma constante:
CREATE POLICY regional_access
ON CATALOG analytics
ROW FILTER filter_by_region
TO 'emea_team'
FOR TABLES
MATCH COLUMNS has_tag('region') AS rgn
USING COLUMNS (rgn, 'emea,apac');
Filtragem de linhas em várias colunas relacionadas
Quando uma tabela possui várias colunas representando atributos relacionados (por exemplo, ship_to_country e bill_to_country), você pode combiná-las com condições de tag separadas e passar ambas para uma única UDF. Isso evita a criação de políticas separadas para cada coluna. Uma política pode incluir até três expressões de coluna na cláusula MATCH COLUMNS (consulte Cotas de política).
CREATE FUNCTION filter_by_countries(ship_country STRING, bill_country STRING, allowed STRING)
RETURNS BOOLEAN
DETERMINISTIC
RETURN array_contains(split(allowed, ','), lower(ship_country))
OR array_contains(split(allowed, ','), lower(bill_country));
CREATE POLICY regional_orders
ON SCHEMA prod.orders
ROW FILTER filter_by_countries
TO analysts
FOR TABLES
WHEN has_tag_value('sensitivity', 'high')
MATCH COLUMNS
has_tag('ship_country') AS ship,
has_tag('bill_country') AS bill
USING COLUMNS (ship, bill, 'us,ca,mx');
Um analista visualiza apenas os pedidos em que o país de envio ou o país de faturamento esteja na lista de países permitidos.
Tabelas de pesquisa em UDFs de política ABAC
Quando as regras de acesso variam por usuário e não podem ser expressas apenas pelas cláusulas TO/EXCEPT da política, você pode verificar os direitos de acesso em uma pequena tabela de pesquisa. Use TO/EXCEPT sempre que possível, pois é a abordagem preferida para segmentar principais (consulte Abordagem para segmentar principais). Mantenha a tabela de pesquisa pequena para que o otimizador converta a subconsulta em uma join hash de broadcast (consulte Manter tabelas de pesquisa pequenas).
CREATE TABLE access_rules (
principal VARCHAR(255),
priority VARCHAR(64)
);
INSERT INTO access_rules VALUES
('alice@company.com', '1-URGENT'),
('alice@company.com', '2-HIGH'),
('bob@company.com', '1-URGENT');
CREATE FUNCTION priority_allowed(o_priority STRING) RETURNS BOOLEAN
RETURN EXISTS (
SELECT 1 FROM access_rules
WHERE principal = session_user() AND priority = o_priority
);
CREATE POLICY priority_filter
ON CATALOG operations
ROW FILTER priority_allowed
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('priority') AS pri
USING COLUMNS (pri);