Padrões comuns para filtragem de linhas e mascaramento de coluna

Esta página descreve padrões comuns para implementar políticas de filtro de linha ABAC e máscara de coluna.

Funções de mascaramento compatíveis com conversão

Azure 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 tipo para máscaras de coluna.

Os padrões a seguir ajudam você a criar funções de mascaramento compatíveis com casting.

Retornar um tipo que possa ser convertido

Ao mascarar uma coluna, retorne o mesmo tipo de dados ou um tipo que pode ser convertido nela. Verifique os tipos de dados das colunas visadas pela política e assegure-se de que cada ramificação da função retorna 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;

Evitar 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 é convertido automaticamente para o tipo da coluna. Se o valor exibido exceder o intervalo do tipo mais restrito, a conversão resultará em estouro e a consulta falhará em runtime.

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

Usar VARIANT para vários tipos de coluna

Veja Mascarar múltiplos tipos de colunas com uma única função.

Compatibilidade de conjunto de testes

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;

Mascarar múltiplos tipos de colunas com uma única função

Um único UDF de mascaramento que aceita e retorna VARIANT pode mascarar colunas de muitos tipos de dados, o que reduz o número de UDFs e políticas que você mantém. O Azure Databricks projeta o valor da coluna para VARIANT antes da execução da função, depois projeta o valor de retorno de volta para o tipo da coluna seguindo as regras ANSI SQL.

Dentro da função, use schema_of_variant() para inspecionar o valor e ramificar com base no tipo dele. Em cada ramo, retorne um valor que possa ser convertido para o tipo da coluna de destino.

Mascarar múltiplos tipos numéricos

A VARIANT máscara mais simples retorna uma única constante que o Azure Databricks converte para o tipo de cada coluna. Uma política usando essa função pode mascarar INT, DOUBLE, e DECIMAL colunas, 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, faça a ramificação com base em schema_of_variant() e retorne um valor apropriado para cada tipo:

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;

Os tipos inteiros se alargam para BIGINT em um VARIANT, portanto ramificam em BIGINT vez de INT ou TINYINT.

Mascarar colunas STRUCT, ARRAY e MAP

A VARIANT abordagem também mascara colunas de tipo complexo, estendendo o exemplo numérico acima para dados aninhados. Uma coluna STRUCT, ARRAY ou MAP é passada para a função como VARIANT, e o Azure Databricks converte o que a função retorna de volta para o tipo declarado da coluna. O valor retornado deve ter o mesmo esquema que a entrada, ou a conversão falha e a consulta gera um erro.

Observação

STRUCT colunas são suportadas no Databricks Runtime 18.1 e versões posteriores. As colunas ARRAY e MAP são compatíveis com o Databricks Runtime 19 e versões superiores. Esse casting funciona apenas dentro das políticas de máscara de coluna e filtro de linha ABAC, não em máscaras SQL ou em nível de tabela em geral.

O exemplo a seguir de função mascara tipos específicos STRUCT, ARRAY, e MAP . Adicione um WHEN branch 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 WHEN ramo faz duas coisas, descritas nas seções a seguir:

  1. Identifique o esquema VARIANT. Cada esquema VARIANT precisa de sua própria lógica de mascaramento, então a função se divide com base na string do esquema que schema_of_variant(val) retorna para aplicar a ocultação correta a cada esquema.
  2. Reconstrua o valor mascarado. Crie um valor ocultado do tipo da coluna e envolva-o em to_variant_object() para retornar um VARIANT.

Um valor cujo esquema não corresponde a nenhum ramo cai para ELSE.

Identificar o esquema VARIANT

Faça a correspondência com o esquema do valor VARIANT, não com o tipo declarado da coluna: a conversão para VARIANT normaliza os dados, portanto o esquema pode diferir da definição da coluna. Os exemplos a seguir mostram casos comuns:

Tipo de coluna resultado schema_of_variant() Notes
ARRAY<STRING> ARRAY<STRING>
ARRAY<INT> ARRAY<BIGINT> Tipos inteiros se ampliam para BIGINT.
STRUCT<id: INT, name: STRING> OBJECT<id: BIGINT, name: STRING> as colunas STRUCT e MAP passam a ser OBJECT.
STRUCT<name: STRING, id: INT> OBJECT<id: BIGINT, name: STRING> Os campos são ordenados alfabeticamente por chave, não por ordem de declaração.
ARRAY<STRUCT<id: INT, name: STRING>> ARRAY<OBJECT<id: BIGINT, name: STRING>>
MAP<STRING, STRING> Decisão {a: 'x'} OBJECT<a: STRING> Varia conforme a linha: as chaves de cada linha determinam o esquema.
MAP<STRING, STRING> Decisão {a: 'x', b: 'y'} OBJECT<a: STRING, b: STRING> Uma linha diferente da mesma coluna produz um esquema diferente.
MAP<STRING, STRING> Decisão {a: null} OBJECT<a: VOID> Um valor nulo torna-se VOID.
MAP<STRING, STRUCT<id: INT, name: STRING>> Decisão {a: ...} OBJECT<a: OBJECT<id: BIGINT, name: STRING>, ...>

Reconstrua o valor mascarado

Para cada esquema que você corresponder, construa um valor redigido com a mesma estrutura e depois o envolva em to_variant_object():

  • Use named_struct() para reconstruir um STRUCT, mantendo os campos que você quer e substituindo o restante.
  • Use array() para reconstruir um ARRAY.
  • Use map() para reconstruir um MAP com valores redigidos.

O Azure Databricks converte o valor retornado VARIANT de volta para o tipo declarado da coluna, portanto cada valor reconstruído 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 consulta simples para confirmar a saída mascarada. A função a seguir mascara uma ARRAY<STRUCT<id: BIGINT, value: FLOAT>> coluna 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 reconverter o VARIANT mascarado de volta para o tipo da coluna. Isso reflete o que a política faz em tempo de execução:

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, GEOGRAPHY, ou TIME não pode ser mascarada com VARIANT.
  • MAP As chaves devem ser STRING. Colunas digitadas MAP<INT, ...>, MAP<DATE, ...>, e assim por diante não são convertidas.

Impedir o acesso até que colunas confidenciais sejam marcadas

Um padrão de governança comum é controlar o acesso com base em se os dados foram classificados. Você pode implementá-lo com uma marca restritiva padrão e políticas que impõem diferentes níveis de proteção, dependendo do status de classificação.

  1. Aplique uma marca como classification : unverified a todos os novos objetos por padrão, por meio da automação ou por meio da herança da marca aplicando a marca no nível de catálogo ou esquema, de modo que quaisquer novas tabelas adicionadas ao catálogo ou esquema herdem automaticamente a marca.
  2. Crie uma política de filtro de linha que bloqueia o acesso a tabelas marcadas classification : unverified.
  3. Crie uma política de máscara para coluna que oculte colunas confidenciais em tabelas onde a tag classification : unverified não está mais presente.
  4. Quando um administrador de dados conclui a classificação, ele atualiza a etiqueta. A política de bloqueio não corresponde mais 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 confidenciais depois de classificados, defina uma política de máscara de coluna que se aplica quando a marca 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 regex

Revele parte de um valor confidencial usando operações de cadeia de caracteres em vez de regex. O mascaramento baseado em Regex verifica todo o valor de cada linha, que é caro em campos de texto grandes (consulte Evitar máscara regex 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)

O hash consistente (também chamado de pseudonimização determinística) substitui dados confidenciais por um valor hash que é o mesmo em várias tabelas. Marcar uma função informa DETERMINISTIC ao mecanismo que a função sempre retorna o mesmo resultado para a mesma entrada, o que a ajuda a otimizar a consulta. Consulte Usar expressões determinísticas e protegidas contra erros.

A função a seguir faz um hash consistente de um valor de string e usa um parâmetro version para dar suporte à rotação de chaves. Incremente o número version mediante a cláusula USING COLUMNS da política para gerar novos hashes sem afetar dados históricos que usaram a versão anterior. A função concatena o valor original com o número de versão antes do 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 está consultando

Importante

Os atributos de identidade nas políticas ABAC estão em Beta. Para usá-los, um administrador de conta deve:

Uma política de máscara de coluna pode usar os atributos de identidade do usuário que consulta para mascarar dados sensíveis sem exigir grupos dedicados. Por exemplo, ele pode manter dados não mascarados para usuários com department = HR e mascará-los para todos os outros.

Esses padrões exigem atributos de identidade fornecidos aos seus usuários pelo seu provedor de identidade, e as funções se comportam de forma diferente das condições apenas de tags, afetando a forma como você escreve a política. Antes de usá-los, consulte as funções de atributos de identidade e atributos de identidade.

Importante

As funções resultam em false quando o usuário não tem valor para o atributo ou quando a chave do atributo não existe. Escreva a condição de forma que esse false resultado restrinja o acesso em vez de concedê-lo. Negue a correspondência com NOT para que a máscara se aplique, a menos que o atributo coincida. 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, os usuários sem o atributo de departamento também são mascarados. Evite o contrário: uma condição que só mascare quando o atributo coincide com usuários folhas que não tenham valor para o atributo desmascarado.

Para comportamento de avaliação, veja Condições de atributo de identidade. Para limitações, veja Atributos de Identidade em condições de política.

Corresponda a um valor fixo

Mascare 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 o atributo de departamento ambos veem a máscara.

Comparar com uma marcação governada

O exemplo anterior nomeia um valor de atributo específico (HR) na política, então 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 compare o atributo do department usuário que faz a consulta com essa tag. A coluna é revelada apenas quando o departamento do usuário corresponde ao valor da dept_tag 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;

Chaves de atributo e valores diferenciam maiúsculas de minúsculas, e os valores são comparados exatamente: Finance e finance não coincidem.

Restringa o acesso para agentes externos que atuam em nome de um usuário

Importante

Os atributos de contexto nas políticas ABAC estão em Beta. Para usá-los, um administrador de conta deve ativar a prévia dos Atributos de Contexto do UC ABAC na página de Pré-visualizações do console da conta. Consulte Gerenciar visualizações no nível da conta.

Atributos de contexto podem ser usados para restringir o acesso a dados para requisições feitas em nome do usuário por meio de uma aplicação OAuth (autorização de usuário para máquina (U2M)). Se os agentes estiverem conectados via OAuth, essa configuração pode ser usada para impedir que eles acessem dados quando agem em nome de um usuário, mesmo que o usuário ainda possa ler os dados ao consultá-los diretamente no espaço de trabalho.

Qualquer acesso autenticado por OAuth através da CLI do Azure Databricks, dos SDKs ou da API de Execução de Instruções SQL define request.is_on_behalf_of para 'true', mesmo quando o usuário está consultando manualmente. O acesso autenticado com um token de acesso pessoal (PAT) não tem isso. O acesso ao Genie não pode ser registrado por meio desse mecanismo.

Esses padrões utilizam as funções de atributo de contexto. Para atributos e comportamentos disponíveis, veja Funções de atributos de contexto (Beta).

Conecte o agente a Azure Databricks

Para usar atributos de contexto, conecte o agente usando uma aplicação OAuth personalizada:

  1. Um administrador de conta ativa a pré-visualização dos Atributos de Contexto UC ABAC a partir do console da conta. Consulte Gerenciar visualizações do Azure Databricks.
  2. Um administrador de conta registra um aplicativo OAuth personalizado no console da conta e registra seu ID de cliente.
  3. Conecte o agente ao MCP gerenciado pelo Azure Databricks por meio dessa aplicação OAuth. Veja Configurar um cliente OAuth personalizado.

Um agente que usa o cliente integrado databricks-cli 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 ambas compartilham o ID de cliente databricks-cli. Para governar uma aplicação específica, registre uma aplicação OAuth personalizada e conecte o agente por meio dela.

Warning

Certifique-se de que um agente não possa acessar os dados por um caminho que sua apólice 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 autenticar com um PAT. Um PAT não define request.is_on_behalf_of como 'true', então uma condição sobre esse 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 não esteja coberto pela condição, como o cliente genérico databricks-cli.

Mascarar uma coluna para solicitações em nome de outra pessoa

Mascare ssn para solicitações executadas em nome de um usuário, como um agente agindo por meio de um aplicativo OAuth registrado, mantendo-o sem mascaramento para consultas 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 consulta direta retorna valores reais, e uma solicitação em nome de outra entidade exibe os valores mascarados. Usando a CLI e a API de Execução de Instruções SQL, request.is_on_behalf_of também lê 'true', então essa política também mascara a coluna para essas requisições. Para direcionar uma aplicação específica, compare request.client_id com o ID do cliente dessa aplicação.

Restringir uma coluna a um aplicativo aprovado

Mascare ssn para todas as requisições externas, exceto as provenientes 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 aplicação fez uma solicitação, inspecione o identity_metadata.acting_resource campo nos logs de auditoria.

Filtragem de linhas com predicados somente de coluna

Filtrar linhas usando uma lógica booliana simples que referencia apenas colunas de tabela. Os predicados de somente coluna permitem o processamento de predicado, o que possibilita que o mecanismo ignore dados irrelevantes durante as verificações (consulte Entender o processamento de predicado em tabelas protegidas).

CREATE FUNCTION filter_by_region(region STRING, allowed STRING)
RETURNS BOOLEAN
DETERMINISTIC
  RETURN array_contains(split(allowed, ','), lower(region));

Use com uma política que transmita 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 linha em várias colunas relacionadas

Quando uma tabela tem várias colunas que representam atributos relacionados (por exemplo, ship_to_country e bill_to_country), você pode combiná-las com condições de marca 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 vê apenas pedidos em que o país de remessa ou cobrança está em sua lista autorizada.

Tabelas de pesquisa em UDFs de políticas ABAC

Quando as regras de acesso variam por usuário e não podem ser expressas apenas por meio das cláusulas da TO/EXCEPT política, você pode verificar os direitos de acesso em uma tabela de pesquisa pequena. Use TO/EXCEPT quando possível, pois é a abordagem preferencial para entidades principais de destino (consulte Abordagem para entidades principais de destino). Mantenha a tabela de pesquisa pequena para que o otimizador converta a subconsulta em uma junção de hash de transmissão (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);