行フィルター処理と列マスクの一般的なパターン

このページでは、ABAC 行フィルターおよび列マスク ポリシーを実装するための一般的なパターンについて説明します。 全体的な概念については、 属性ベースのアクセス制御 (ABAC) のコア概念を参照してください。 ポリシー構文については、 ABAC ポリシーの作成と管理に関するページを参照してください。

キャスト互換マスク関数

Azure Databricksは、マスク関数の出力をターゲット列のデータ型と一致するように自動的にキャストします。 列マスクの自動型キャストを参照してください。

次のパターンは、キャスト互換のマスク関数を設計するのに役立ちます。

キャスト可能な型を返す

列をマスクする場合は、同じデータ型またはそれにキャスト可能な型を返します。 ポリシーの対象となる列のデータ型を確認し、関数のすべての分岐で互換性のある値が返されることを確認します。

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

数値オーバーフローを回避する

mask 関数が受け入れて、ターゲット列よりも広い数値型を返すと、結果は列の型に自動的にキャストバックされます。 返された値がより狭い型の範囲を超えると、キャストがオーバーフローし、実行時にクエリが失敗します。

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

複数の列の型に VARIANT を使用する

複数の列の種類については、VARIANT ベースのマスク関数を参照してください

テスト キャスト互換性

さまざまなデータ パターンでマスク関数をテストします。

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 ベースのマスク関数

さまざまなデータ型 ( INTDOUBLEDECIMAL(10,2)DECIMAL(15,5)など) の列をマスクする必要がある場合は、 VARIANT 型を受け入れて返す 1 つのマスク UDF を記述できます。 Azure Databricksは、ANSI SQL 標準に従って、ターゲット列のデータ型と一致するように列マスク関数の出力を自動的にキャストします。

この方法により、必要な UDF とポリシーの数が減ります。 列の型ごとに個別のマスク関数を記述する代わりに、1 つの関数ですべての型を処理します。

1 つの関数で複数の数値型をマスクする

数値精度ごとに個別のマスク関数を作成するのではなく、 VARIANT を使用して、それらをすべて 1 つの関数で処理できます。

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

この関数は0VARIANTとして返し、Azure Databricks がそれを自動的にターゲット列の型にキャストします。 この関数を使用した 1 つの ABAC ポリシーでは、それぞれの精度に対して個別の関数を必要とせずに、INTDOUBLE、および DECIMAL の列をマスクできます。

関数内で型を明示的に保持する場合は、型に分岐し、 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;

VARIANT を使用して構造体列をマスクする

Databricks Runtime 18.1 以降では、ABAC ポリシー内の VARIANT にキャストすることで、構造体列をマスクすることもできます。 構造体の図形上で分岐し、フィールドを選択的に編集します。

Note

マスクの VARIANT への構造体のキャストは、ABAC 列マスク ポリシー内でのみサポートされます。

次の例では、 schema_of_variant() を使用して、2 つの異なる構造体図形を識別し、それぞれで機密フィールドを編集します。

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;

機密性の高い列がタグ付けされるまでアクセスを禁止する

一般的なガバナンス パターンは、データが分類されたかどうかに基づいてアクセスを制御することです。 これは、分類の状態に応じて異なるレベルの保護を適用する既定の制限付きタグとポリシーを使用して実装できます。

  1. カタログレベルまたはスキーマ レベルでタグを適用することで、自動化またはタグ継承を通じて、既定ですべての新しいオブジェクトに classification : unverified のようなタグを適用し、カタログまたはスキーマに追加された新しいテーブルが自動的にタグを継承できるようにします。
  2. classification : unverifiedタグ付けされたテーブルへのアクセスをブロックする行フィルター ポリシーを作成します。
  3. classification : unverified タグが存在しなくなったテーブルで機密性の高い列をマスクする列マスク ポリシーを作成します。
  4. データ スチュワードが分類を完了すると、タグが更新されます。 ブロック ポリシーが一致しなくなり、マスク ポリシーが有効になります。
-- 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');

機密データを分類した後で保護するには、 classification : unverified タグが存在しなくなったときに有効な列マスク ポリシーを定義します。

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;

正規表現なしの部分的な表示

正規表現の代わりに文字列操作を使用して、機密性の高い値の一部を表示します。 正規表現ベースのマスクは、すべての行の値全体をスキャンします。これは、大きなテキスト フィールドではコストがかかります (大 きなテキスト フィールドでの正規表現マスクの回避を参照)。

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

一貫性のあるハッシュ (決定論的仮名化)

整合性ハッシュ (決定論的仮名化とも呼ばれます) は、機密データを複数のテーブルで同じハッシュ値に置き換えます。 関数を DETERMINISTIC としてマークすると、関数は常に同じ入力に対して同じ結果を返すので、クエリを最適化するのに役立ちます。 決定的でエラーセーフな式を使用する を参照してください。

次の関数は、文字列値を一貫してハッシュし、 version パラメーターを使用してキーのローテーションをサポートします。 ポリシーのversion句を使用してUSING COLUMNS番号をインクリメントし、以前のバージョンを使用した履歴データを中断せずに新しいハッシュを生成します。 この関数は、ハッシュする前に元の値とバージョン番号を連結するため、同じバージョンの同じ入力で常に同じハッシュが生成されます。

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

列専用の述語を用いる行フィルタリング

テーブル列のみを参照する単純なブール型ロジックを使用して行をフィルター処理します。 列のみの述語では述語プッシュダウンが有効になります。これにより、エンジンはスキャン中に無関係なデータをスキップできます ( 保護されたテーブルの述語プッシュダウンについてを参照してください)。

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

許可されているリージョンを定数として渡すポリシーで使用します。

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

複数の関連する列の行フィルター処理

テーブルに関連する属性 ( ship_to_countrybill_to_countryなど) を表す複数の列がある場合は、それらを個別のタグ条件と照合し、両方を 1 つの UDF に渡すことができます。 これにより、列ごとに個別のポリシーが作成されるのを回避できます。 ポリシーには、 MATCH COLUMNS 句に最大 3 つの列式を含めることができます ( ポリシー クォータを参照)。

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

アナリストは、配送先または請求先の国が許可リストに含まれている注文のみを確認します。

ABAC ポリシー UDF の参照テーブル

アクセス 規則がユーザーごとに異なり、ポリシーの TO/EXCEPT 句だけで表現できない場合は、小さな参照テーブルに対するアクセス権を確認できます。 可能な場合は TO/EXCEPT を使用します。これは、プリンシパルをターゲットにするための推奨されるアプローチです (プリンシパルを ターゲットにするためのアプローチを参照)。 オプティマイザーがサブクエリをブロードキャスト ハッシュ結合に変換するように、ルックアップ テーブルを小さくしておきます ( 参照テーブルを小さくするを参照)。

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