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

Esta página descreve padrões comuns para a implementação de políticas ABAC de filtros de linhas e de máscaras de colunas. Para conceitos gerais, veja Conceitos Centrais para controlo de acesso baseado em atributos (ABAC). Para a sintaxe das políticas, consulte Criar e gerir políticas ABAC.

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

O Azure Databricks converte automaticamente a saída da função de mascaramento para corresponder ao tipo de dados da coluna de destino. Ver: Conversão automática de tipos para máscaras de coluna.

Os padrões seguintes ajudam-no a desenhar funções de mascaramento compatíveis com o cast.

Retorna um tipo convertível

Ao mascarar uma coluna, devolva o mesmo tipo de dado ou um tipo que seja compatível com ela. Verifique os tipos de dados das colunas que a sua política tem como alvo e verifique se cada ramo da função devolve 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 o excesso numérico

Quando uma função de máscara aceita e devolve um tipo numérico mais amplo do que a coluna de destino, o resultado é automaticamente lançado de volta ao tipo da coluna. Se o valor devolvido exceder o intervalo do tipo mais estreito, o cast transborda e a consulta falha 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;

Usar VARIANT para múltiplos tipos de colunas

Veja funções de mascaramento baseadas em VARIANT para múltiplos tipos de colunas.

Compatibilidade de transmissão de teste

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

Funções de mascaramento baseadas em VARIANT para múltiplos tipos de colunas

Quando é necessário mascarar colunas de diferentes tipos de dados (por exemplo, INT, DOUBLE, DECIMAL(10,2), DECIMAL(15,5), etc.), pode escrever um único UDF de mascaramento que aceita e devolve um VARIANT tipo. O Azure Databricks converte automaticamente a saída da função de máscara da coluna para corresponder ao tipo de dados da coluna de destino, seguindo os padrões ANSI SQL.

Esta abordagem reduz o número de UDFs e políticas necessárias. Em vez de escrever funções de mascaramento separadas para cada tipo de coluna, uma função trata todos os tipos.

Mascarar múltiplos tipos numéricos com uma única função

Em vez de criar uma função máscara separada para cada precisão numérica, pode usar VARIANT para lidar com todas com uma única função:

CREATE FUNCTION mask_numeric(val VARIANT)
RETURNS VARIANT
DETERMINISTIC
RETURN 0::VARIANT;

Esta função devolve 0 como um VARIANT, que o Azure Databricks converte automaticamente para o tipo da coluna alvo. Uma única política ABAC usando esta função pode mascarar INT, DOUBLE, e DECIMAL colunas sem exigir funções separadas para cada precisão.

Se preferir preservar o tipo explicitamente dentro da função, pode ramificar-se no tipo e devolver um valor mascarado apropriado para cada um, usando schema_of_variant():

-- Use VARIANT to accommodate different data types
CREATE FUNCTION flexible_mask(data VARIANT)
RETURNS VARIANT
RETURN CASE
  WHEN schema_of_variant(data) = 'INT' 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;

Mascarar colunas de estruturas com VARIANT

Para Databricks Runtime 18.1 e superiores, também pode mascarar colunas de struct convertendo-as para VARIANT dentro de uma política ABAC. Dividir conforme a forma da struct para ocultar seletivamente os campos.

Note

A conversão de estruturas para VARIANT mascaramento é suportada somente nas políticas de máscara de coluna ABAC.

O exemplo seguinte serve schema_of_variant() para identificar duas formas diferentes de estruturas e redigir campos sensíveis em cada uma:

CREATE FUNCTION flexible_mask(data VARIANT)
RETURNS VARIANT
RETURN CASE
WHEN schema_of_variant(data) = 'OBJECT<age: BIGINT, email: STRING>' THEN
  to_variant_object(named_struct('age', data:age, 'email', 'redacted'))
WHEN schema_of_variant(data) = 'OBJECT<id: BIGINT, ssn: STRING>' THEN
  to_variant_object(named_struct('id', data:id, 'ssn', 'xxx-xx-xxxx'))
ELSE NULL::VARIANT
END;

Impedir o acesso até que colunas sensíveis sejam marcadas

Um padrão comum de governação é controlar o acesso com base na classificação dos dados. Pode implementar isto com uma etiqueta restritiva padrão e políticas que impunham diferentes níveis de proteção dependendo do estado da classificação.

  1. Aplicar uma etiqueta como classification : unverified a todos os novos objetos por padrão, através de automação ou por herança de etiquetas, aplicando a etiqueta ao nível do catálogo ou esquema, para que quaisquer novas tabelas adicionadas ao catálogo ou esquema herdem a etiqueta automaticamente.
  2. Crie uma política de filtro de linhas que bloqueie o acesso a tabelas etiquetadas classification : unverified.
  3. Crie uma política de máscara de coluna que oculte colunas sensíveis em tabelas onde a classification : unverified etiqueta já não está presente.
  4. Quando um gestor de dados conclui a classificação, atualiza a etiqueta. A política de bloqueio já não corresponde, e a política de uso de máscara 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 a sua classificação, defina-se uma política de máscara de coluna que entra em vigor quando a classification : unverified etiqueta já não está 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

Revela parte de um valor sensível usando operações de string em vez de regex. O mascaramento baseado em regex varre o valor total para cada linha, o que é dispendioso em campos de texto grandes (ver Evitar mascaramento 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 hashing consistente (também chamado de pseudonimização determinística) substitui dados sensíveis por um valor hashado que é o mesmo em várias tabelas. Marcar uma função como DETERMINISTIC indica ao motor que a função retorna sempre o mesmo resultado para a mesma entrada, o que ajuda a otimizar a consulta. Veja Usar expressões determinísticas e seguras contra erros.

A função seguinte faz hash consistente de um valor de cadeia e utiliza um version parâmetro para suportar a rotação de chaves. Incremente o version número através da USING COLUMNS cláusula da política para gerar novos hashes sem interromper 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, por isso a mesma entrada com a mesma versão produz sempre o mesmo hash.

CREATE FUNCTION pseudonymize(val STRING, version INT) RETURNS STRING
DETERMINISTIC
  RETURN SHA2(CONCAT(val, CAST(version AS STRING)), 256);

Filtragem por linhas com predicados apenas em colunas

Filtrar linhas usando lógica booleana simples que apenas faz referência às colunas da tabela. Predicados apenas em colunas permitem o "predicate pushdown", o que permite ao motor ignorar dados irrelevantes durante os escanços (ver Compreender o "predicate pushdown" 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 passe as regiões autorizadas como 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 entre múltiplas colunas relacionadas

Quando uma tabela tem várias colunas que representam atributos relacionados (por exemplo, ship_to_country e bill_to_country), pode correspondê-las com condições de etiqueta separadas e passar ambas para um único UDF. Isto evita a criação de políticas separadas para cada coluna. Uma política pode incluir até três expressões de coluna na MATCH COLUMNS cláusula (ver quotas 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 só vê encomendas em que o país de envio ou de faturação está na lista dos países permitidos.

Tabelas de consulta em UDFs de políticas ABAC

Quando as regras de acesso variam consoante o utilizador e não podem ser expressas apenas através das cláusulas da TO/EXCEPT política, pode verificar os direitos de acesso numa pequena tabela de consulta. Use TO/EXCEPT sempre que possível, pois é a abordagem preferida para direcionar os principais (ver Abordagem para direcionar os principais). Mantenha a tabela de consulta pequena para que o otimizador converta a subconsulta numa junção de hash broadcast (veja Manter tabelas de consulta 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);