Vanliga mönster för radfiltrering och kolumnmaskering

På den här sidan beskrivs vanliga mönster för implementering av ABAC-radfilter och kolumnmaskprinciper. Övergripande begrepp finns i Grundläggande begrepp för attributbaserad åtkomstkontroll (ABAC). Principsyntax finns i Skapa och hantera ABAC-principer.

Cast-kompatibla maskeringsfunktioner

Azure Databricks automatiskt omvandlar maskeringsfunktionens utdata så att de matchar målkolumnens datatyp. Se Automatisk typgjutning för kolumnmasker.

Följande mönster hjälper dig att utforma cast-kompatibla maskeringsfunktioner.

Returnera en typ möjligt att konvertera

När du maskerar en kolumn returnerar du samma datatyp eller en typ som kan castas till den. Kontrollera datatyperna för kolumnerna som dina principmål har och kontrollera att varje gren av funktionen returnerar ett kompatibelt värde.

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

Undvik numeriskt överflöde

När en maskfunktion accepterar och returnerar en bredare numerisk typ än målkolumnen kastas resultatet automatiskt tillbaka till kolumnens typ. Om det returnerade värdet överskrider intervallet för den begränsade typen, flödar kastet över och frågan misslyckas under körning.

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

Använda VARIANT för flera kolumntyper

Se VARIANT-baserade maskeringsfunktioner för flera kolumntyper.

Testa kompatibilitet för streaming

Testa maskeringsfunktioner med olika datamönster.

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;

VARIANT-baserade maskeringsfunktioner för flera kolumntyper

När du behöver maskera kolumner med olika datatyper (till exempel INT, , DOUBLEDECIMAL(10,2), , DECIMAL(15,5)och så vidare) kan du skriva en enda maskerings-UDF som accepterar och returnerar en VARIANT typ. Azure Databricks automatiskt omvandlar kolumnmaskfunktionens utdata så att de matchar målkolumnens datatyp enligt ANSI SQL-standarder.

Den här metoden minskar antalet UDF:er och principer som behövs. I stället för att skriva separata maskeringsfunktioner för varje kolumntyp hanterar en funktion alla typer.

Maskera flera numeriska typer med en enda funktion

I stället för att skapa en separat maskfunktion för varje numerisk precision kan du använda VARIANT för att hantera dem alla med en enda funktion:

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

Den här funktionen returnerar 0 som en VARIANT, vilket Azure Databricks automatiskt kastar till målkolumnens typ. En enskild ABAC-princip som använder den här funktionen kan maskera INT, DOUBLEoch DECIMAL kolumner utan att kräva separata funktioner för varje precision.

Om du föredrar att bevara typen explicit i funktionen kan du förgrena på typen och returnera ett lämpligt maskerat värde för var och en med hjälp av 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;

Maskera structkolumner med VARIANT

För Databricks Runtime 18.1 och senare kan du också maskera structkolumner genom att göra om dem till VARIANT inom en ABAC-policy. Anpassa sig efter strukturen av structen för att selektivt dölja eller ta bort fält:

Note

Att konvertera structs till VARIANT för maskering stöds endast inom ABAC-kolumnmaskprinciper.

I följande exempel används schema_of_variant() för att identifiera två olika structformer och redigera känsliga fält i var och en:

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;

Förhindra åtkomst tills känsliga kolumner har taggats

Ett vanligt styrningsmönster är att kontrollera åtkomsten baserat på om data har klassificerats. Du kan implementera detta med en standardbegränsande tagg och principer som tillämpar olika skyddsnivåer beroende på klassificeringsstatusen.

  1. Använd en tagg som classification : unverified alla nya objekt som standard, via automatisering eller genom taggarv genom att tillämpa taggen på katalog- eller schemanivå, så att alla nya tabeller som läggs till i katalogen eller schemat automatiskt ärver taggen.
  2. Skapa en radfilterprincip som blockerar åtkomst till tabeller taggade classification : unverified.
  3. Skapa en kolumnmaskprincip som maskerar känsliga kolumner i tabeller där taggen classification : unverified inte längre finns.
  4. När en dataförvaltare slutför klassificeringen uppdaterar de taggen. Blockeringsprincipen matchar inte längre och maskeringsprincipen börjar gälla.
-- 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');

Om du vill skydda känsliga data efter att de har klassificerats definierar du en kolumnmaskprincip som börjar gälla när taggen classification : unverified inte längre finns:

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;

Partiell avslöjande utan regex

Visa en del av ett känsligt värde med hjälp av strängåtgärder i stället för regex. Regex-baserad maskering söker igenom hela värdet för varje rad, vilket är dyrt för stora textfält (se Undvik regexmaskering på stora textfält).

CREATE FUNCTION mask_ssn(ssn STRING, show_last INT) RETURNS STRING
DETERMINISTIC
  RETURN CONCAT('***-**-', RIGHT(ssn, show_last));

Konsekvent hashning (deterministisk pseudonymisering)

Konsekvent hashning (kallas även deterministisk pseudonymisering) ersätter känsliga data med ett hashvärde som är detsamma i flera tabeller. Markera en funktion som DETERMINISTIC talar om för motorn att funktionen alltid returnerar samma resultat för samma indata, vilket hjälper den att optimera frågan. Se Använda deterministiska, felsäkra uttryck.

Följande funktion hashar konsekvent ett strängvärde och använder en version parameter för att stödja nyckelrotation. Öka version talet genom en klausul i policyn USING COLUMNS för att generera nya hashvärden utan att bryta historiska data som använde den tidigare versionen. Funktionen sammanfogar det ursprungliga värdet med versionsnumret före hashning, så samma indata med samma version genererar alltid samma hash.

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

Radfiltrering med endast kolumnpredikat

Filtrera rader med enkel boolesk logik som endast refererar till tabellkolumner. Endast kolumnpredikat aktiverar predikat-pushdown, vilket gör att motorn kan hoppa över irrelevanta data under genomsökningar (se Förstå predikat-pushdown på skyddade tabeller).

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

Använd med en policy som överför de godkända regionerna som en konstant.

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

Radfiltrering över flera relaterade kolumner

När en tabell har flera kolumner som representerar relaterade attribut (till exempel ship_to_country och bill_to_country), kan du matcha dem med separata taggvillkor och skicka båda till en enda UDF. Detta undviker att skapa separata principer för varje kolumn. En princip kan innehålla upp till tre kolumnuttryck i MATCH COLUMNS -satsen (se Principkvoter).

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

En analytiker ser bara beställningar där leverans- eller faktureringslandet finns i listan över tillåtna.

Uppslagstabeller i ABAC-policy UDF:er

När åtkomstreglerna varierar per användare och inte kan uttryckas enbart via principens satser kan du kontrollera åtkomsträttigheterna mot en liten uppslagstabell TO/EXCEPT . Använd TO/EXCEPT när det är möjligt, eftersom det är den bästa metoden för att rikta in sig på huvudkonton (se Metod för att rikta in sig på huvudkonton). Håll uppslagstabellen liten så att optimeraren konverterar underfrågan till en sändningshashkoppling (se Behåll uppslagstabeller små).

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