行フィルターと列マスクを手動で適用する
このページでは、行フィルター、列マスク、マッピング テーブルを使用してテーブル内の機密データをフィルター処理するためのガイダンスと例を示します。これらの機能には Unity Catalog が必要です。
フィルタリングとマスキングのための集中型のタグベースのアプローチをお探しの場合は、 Unity Catalog の属性ベースのアクセス制御 (ABAC)を参照してください。ABAC を使用すると、管理されたタグを使用してポリシーを管理し、多くのテーブルにわたって一貫してポリシーを適用できます。
始める前に
テーブルに行フィルターと列マスクを追加するには、次のものが必要です。
- Unity Catalog が有効になっているワークスペース。
- Unity Catalogに登録されているSQL UDF 。 Python または Scala ロジックを使用するには、まず Python または Scala UDF を作成し、次にそれを呼び出す SQL UDF を作成します。SQL UDF は、行フィルターまたは列マスクとして適用するものです。例については、 「Python UDF を使用した列マスク」を参照してください。UDF のベスト プラクティスと制限については、 「行フィルターと列マスク」を参照してください。
また、次の要件を満たす必要があります。
- 行フィルターまたは列マスクをテーブルに追加する関数を割り当てるには、関数の
EXECUTE権限、スキーマのUSE SCHEMA権限、親カタログのUSE CATALOG権限が必要です。 - 新しい テーブルの作成時にフィルターまたはマスクを追加する場合は、スキーマに対する
CREATE TABLE権限が必要です。 - 既存の テーブルにフィルターまたはマスクを追加する場合は、テーブルの所有者であるか、テーブルに対する
MANAGE権限を持っている必要があります。
行フィルターや列マスクのあるテーブルにアクセスするには、コンピュートリソースが次の要件のいずれかを満たしている必要があります。
- SQLウェアハウス。
- Databricks Runtime 12.2 LTS 以降の標準アクセス モード (以前の共有アクセス モード)。
- Databricks Runtime 15.4 LTS 以降の専用アクセス モード (以前のシングル ユーザー アクセス モード)。
Databricks Runtime 15.3以下では、専用コンピュートを使用して行フィルターや列マスクを読み込むことはできません。
Databricks Runtime 15.4 LTS以降で提供されるデータ フィルターを利用するには、行フィルターと列マスクをサポートするデータ フィルター機能がサーバーレス コンピュートで実行されるため、ワークスペースがサーバーレス コンピュートに対して有効になっていることも確認する必要があります。 行フィルターまたは列マスクを使用するテーブルを読み取るために専用アクセス モードとして構成されたコンピュートを使用する場合、サーバーレス コンピュート リソースに対して料金が発生する場合があります。 これらのテーブルへの書き込み操作は、Databricks Runtime 16.3 以降でのみサポートされており、 MERGE INTOなどのサポートされているパターンを使用する必要があります。専用コンピュートのきめ細かいアクセス制御を参照してください。
行フィルターと列マスクは、テーブルを置き換えるときに保持されます。
REPLACE TABLEを実行すると、スキーマの変更に関係なく、既存の行フィルターが保持されます。新しいテーブルに、元のテーブルにマスクがあった列と同じ名前の列が含まれている場合も、列マスクは保持されます。どちらの場合も、ポリシーは明示的に再定義されていなくても保持されます。これにより、データ アクセス ポリシーが誤って失われるのを防ぎます。
ただし、保持ポリシーが削除または変更された列を参照している場合、後続のクエリは失敗する可能性があります。これを解決するには、 を使用してポリシーを更新または削除します ALTER TABLE。
行フィルターを適用する
行フィルターを作成するには、フィルターポリシーを定義する関数 (UDF) を記述し、それをテーブルに適用します。各テーブルに設定できる行フィルターは1つだけです。行フィルターは0個以上の入力パラメーターを受け入れます。各入力パラメーターは対応する表の1つの列にバインドされます。
行フィルターは、カタログ エクスプローラーまたは SQL コマンドを使用して適用できます。カタログ エクスプローラーの手順では、関数を既に作成し、Unity Catalog に登録していることを前提としています。SQL 命令には、行フィルター関数を作成してテーブルに適用する例が含まれています。
Lakeflow Spark宣言型パイプラインを使用している場合は、 Lakeflow Spark宣言型パイプラインPython API使用して、行フィルターと列マスクを使用するストリーミング テーブルまたはマテリアライズドビューを作成できます。 「行フィルターと列マスクを使用してテーブルを公開する」を参照してください。
- Catalog Explorer
- SQL
- Databricks ワークスペースで、
カタログ をクリックします。
- フィルタリングするテーブルを参照または検索します。
- [ 概要 ] タブの [行フィルター] で、[ フィルターの追加 ] をクリックします。
- [ 行フィルターを追加 ] ダイアログで、フィルター機能を含むカタログとスキーマを選択し、関数を選択します。
- 展開されたダイアログで関数定義を表示して、関数ステートメントに含まれる列と一致するテーブル列を選択します。
- [ 追加 ] をクリックします。
テーブルからフィルターを削除するには、[ fx行フィルター ] をクリックして [ 削除 ] をクリックします。
行フィルターを作成して既存のテーブルに追加するには、 CREATE FUNCTION を使用し、 を使用して関数を適用 ALTER TABLE。を使用してテーブルを作成するときに関数を適用することもできます CREATE TABLE。
-
行フィルターを作成します。
SQLCREATE FUNCTION <function_name> (<parameter_name> <parameter_type>, ...)
RETURN {filter clause whose output must be a boolean}; -
列名を使用してテーブルに行フィルターを適用します。
SQLALTER TABLE <table_name> SET ROW FILTER <function_name> ON (<column_name>, ...);
その他の構文の例 :
-
関数パラメーターと一致する定数リテラルを使用して、行フィルターをテーブルに適用します。
SQLALTER TABLE <table_name> SET ROW FILTER <function_name> ON (<constant_literal>, ...); -
テーブルから行フィルターを削除する
SQLALTER TABLE <table_name> DROP ROW FILTER; -
行フィルターを変更する
SQLRun a DROP FUNCTION statement to drop the existing function, or use CREATE OR REPLACE FUNCTION to replace it. -
行フィルターを削除する
SQLALTER TABLE <table_name> DROP ROW FILTER;
DROP FUNCTION <function_name>;
関数を削除する前に、 ALTER TABLE ... DROP ROW FILTER コマンドを実行する必要があります。そうしないと、テーブルはアクセスできない状態になります。
この方法でテーブルにアクセスできなくなった場合は、テーブルを変更し、ALTER TABLE <table_name> DROP ROW FILTER;を使用して孤立行フィルターの参照を削除してください。
ROW FILTER節も参照してください。
行フィルターの例
この例では、USリージョンのadminグループのメンバーに適用されるSQLユーザー定義関数を作成します。
このサンプル関数をsalesテーブルに適用すると、adminグループのメンバーはテーブル内のすべてのレコードにアクセスできます。関数が管理者以外の人によって呼び出された場合、RETURN_IF条件は失敗し、region='US'式が評価され、USリージョンのレコードのみが表示されるようにテーブルがフィルタリングされます。
CREATE FUNCTION us_filter(region STRING)
RETURN IF(IS_ACCOUNT_GROUP_MEMBER('admin'), true, region='US');
関数を行フィルターとしてテーブルに適用します。salesテーブルからの後続のクエリでは、行のサブセットが返されます。
CREATE TABLE sales (region STRING, id INT);
ALTER TABLE sales SET ROW FILTER us_filter ON (region);
行フィルターを無効にします。今後、salesテーブルからユーザーがクエリーを実行すると、テーブル内のすべての行が返されます。
ALTER TABLE sales DROP ROW FILTER;
CREATE TABLEステートメントの一部として、行フィルターとして関数を適用したテーブルを作成します。salesテーブルからの以降のクエリーは、それぞれ行のサブセットを返します。
CREATE TABLE sales (region STRING, id INT)
WITH ROW FILTER us_filter ON (region);
列マスクを適用する
列マスクを適用するには、関数 (UDF) を作成し、それをテーブル列に適用します。
列マスクは、カタログ・エクスプローラーまたは SQL コマンドを使用して適用できます。カタログ エクスプローラーの手順では、関数を既に作成し、Unity Catalog に登録していることを前提としています。SQL 命令には、列マスク関数を作成し、それをテーブル列に適用する例が含まれています。
Lakeflow Spark宣言型パイプラインを使用している場合は、 Lakeflow Spark宣言型パイプラインPython API使用して、行フィルターと列マスクを使用するストリーミング テーブルまたはマテリアライズドビューを作成できます。 「行フィルターと列マスクを使用してテーブルを公開する」を参照してください。
- Catalog Explorer
- SQL
- Databricks ワークスペースで、
カタログ をクリックします。
- テーブルを参照または検索します。
- [ 概要 ] タブで、列マスクを適用する行を見つけて、[
マスク ] 編集アイコンをクリックします。
- [ 列マスクを追加 ] ダイアログで、フィルター関数を含むカタログとスキーマを選択し、関数を選択します。
- 展開されたダイアログで、関数定義を表示します。マスクされる列に加えて、関数にパラメーターが含まれている場合は、それらの追加関数パラメーターをキャストするテーブル列を選択します。
- [ 追加 ] をクリックします。
テーブルから列マスクを削除するには、テーブル行の fx列マスク をクリックし、[ 削除 ] をクリックします。
列マスクを作成して既存のテーブル列に追加するには、CREATE FUNCTIONを使用し、ALTER TABLEを使用してマスキング関数を適用します。CREATE TABLEを使用してテーブルを作成するときに関数を適用することもできます。
SET MASKを使用してマスキング機能を適用します。MASK句内では、Databricks の組み込みランタイム関数のいずれかを使用するか、他のユーザー定義関数を呼び出すことができます。一般的なユースケースには、 を使用して関数を実行している呼び出し元ユーザーの ID を検査したり、 current_user( ) を使用してメンバーであるグループを取得したりすることが含まれます is_account_group_member( )。詳細については、「 列 mask 句 」および 「組み込み関数」を参照してください。
-
列マスクを作成します。
SQLCREATE FUNCTION <function_name> (<parameter_name> <parameter_type>, ...)
RETURN {expression with the same type as the first parameter}; -
既存のテーブルの列に列マスクを適用する
SQLALTER TABLE <table_name> ALTER COLUMN <col_name> SET MASK <mask_func_name> USING COLUMNS <additional_columns>;
その他の構文の例 :
-
関数パラメーターと一致する定数リテラルを使用して、既存のテーブルの列に列マスクを適用します。
SQLALTER TABLE <table_name> ALTER COLUMN <col_name> SET MASK <mask_func_name> USING COLUMNS (<constant_name>, ...); -
テーブル内の列から列マスクを削除する
SQLALTER TABLE <table_name> ALTER COLUMN <column where mask is applied> DROP MASK; -
既存の関数
DROPか、CREATE OR REPLACE TABLEを使用して列マスクを変更します。 -
列マスクを削除する
SQLALTER TABLE <table_name> ALTER COLUMN <column where mask is applied> DROP MASK;
DROP FUNCTION <function_name>;
関数を削除する前にALTER TABLEコマンドを実行する必要があります。そうしないと、テーブルにアクセスできない状態になります。
この方法でテーブルにアクセスできなくなった場合は、テーブルを変更し、 を使用して孤立したマスク参照を削除 ALTER TABLE <table_name> ALTER COLUMN <column where mask is applied> DROP MASK;を使用します。
列マスクの例
この例では、ssn列をマスクするユーザー定義関数を作成して、HumanResourceDeptグループのメンバーであるユーザーのみがその列の値を表示できるようにします。
CREATE FUNCTION ssn_mask(ssn STRING)
RETURN CASE WHEN is_account_group_member('HumanResourceDept') THEN ssn ELSE '***-**-****' END;
新しい関数を列マスクとしてテーブルに適用します。列マスクは、テーブルの作成時または後で追加できます。
--Create the `users` table and apply the column mask in a single step:
CREATE TABLE users (
name STRING,
ssn STRING MASK ssn_mask);
--Create the `users` table and apply the column mask after:
CREATE TABLE users
(name STRING, ssn STRING);
ALTER TABLE users ALTER COLUMN ssn SET MASK ssn_mask;
クエリーを実行するユーザーがHumanResourceDeptグループのメンバーでない場合、そのテーブルに対するクエリーではマスクされたssn列の値が返されるようになりました。
SELECT * FROM users;
James ***-**-****
列マスクを無効にして、クエリーがssn列の元の値を返すようにするには、次の手順を実行します。
ALTER TABLE users ALTER COLUMN ssn DROP MASK;
Python UDFによる列マスク
列マスクで Python または Scala ロジックを使用するには、Python または Scala UDF を作成し、それを SQL UDF でラップする必要があります。SQL ラッパー関数は、列マスクとして適用するものです。
この例では、E メール アドレスをマスクするPython UDFを作成し、それをSQL UDFでラップします。
-- Step 1: Create the Python UDF with masking logic
CREATE OR REPLACE FUNCTION email_mask_python(email STRING)
RETURNS STRING
LANGUAGE PYTHON
AS $$
import re
return re.sub(r'^[^@]+', lambda m: '*' * len(m.group()), email)
$$;
-- Step 2: Create a SQL wrapper function that calls the Python UDF
CREATE OR REPLACE FUNCTION email_mask_sql(email STRING)
RETURN email_mask_python(email);
次に、SQL ラッパーを列マスクとしてテーブルに適用します。
-- Create the `contacts` table and apply the SQL wrapper as the column mask
CREATE TABLE contacts (
name STRING,
email STRING MASK email_mask_sql);
Python UDF を直接適用するのではなく、SQL ラッパー関数 ( email_mask_sql ) を列マスクとして適用する必要があります。Python UDF ( email_mask_python ) を列マスクとして直接使用しようとすると、 [ROUTINE_NOT_FOUND]エラーが発生します。
ネストされたSTRUCTフィールドの列マスク
ネストされたSTRUCT列に列マスクを適用して、他のフィールドを保持しながら、構造内の特定のフィールドを選択的にマスクすることができます。これは、 STRUCT公開データと機密データの両方が含まれており、ユーザー属性に基づいて個々のフィールドに異なるアクセス制御を適用する場合に役立ちます。
ネストされたフィールドをマスクするには、 named_struct()を使用して STRUCT を再構築し、他のフィールドをそのままにしながら、機密フィールドの値を条件付きで置き換えるマスキング関数を作成します。
この例では、パブリックvalueフィールドと機密secretフィールドの両方を含む STRUCT 列のマスキング関数を作成します。マスキング機能はis_account_group_member()を使用して、完全なデータを表示するか、機密フィールドをマスクするかを決定します。
-- Create a masking function for nested STRUCT fields
CREATE FUNCTION mask_nested_field(data STRUCT<value: STRING, secret: STRING>)
RETURN IF(
is_account_group_member('privileged_users'),
data,
named_struct('value', data.value, 'secret', 'REDACTED')
);
STRUCT 列を持つテーブルを作成するときにマスキング関数を適用します。
-- Create a table with a masked STRUCT column
CREATE TABLE sensitive_data (
id INT,
nested_column STRUCT<value: STRING, secret: STRING>
MASK mask_nested_field
);
-- Insert sample data
INSERT INTO sensitive_data VALUES
(1, named_struct('value', 'public_info', 'secret', 'private_info')),
(2, named_struct('value', 'general_data', 'secret', 'confidential_data'));
テーブルをクエリしてマスキングをテストします。結果はグループのメンバーシップによって異なります。ユーザーがprivileged_usersのメンバーでない場合、シークレットは編集されます。
-- As a non-member of 'privileged_users'
SELECT * FROM sensitive_data;
1 {"value":"public_info","secret":"REDACTED"}
2 {"value":"general_data","secret":"REDACTED"}
既存のテーブルにマスクを適用することもできます。
-- Apply mask to existing STRUCT column
ALTER TABLE sensitive_data
ALTER COLUMN nested_column
SET MASK mask_nested_field;
マスキング関数は、マスクされた列と同じ STRUCT 型の値を返す必要があります。これにより、 INSERT 、 MERGE 、 UPDATE操作中に発生する可能性のある混乱を招くスキーマの不一致を回避できます。この例では、関数は列タイプに一致するSTRUCT<value: STRING, secret: STRING>を返します。
マッピング・テーブルを使用してアクセス制御リストを作成する
行レベルのセキュリティーを実現するには、マッピング・テーブル (またはアクセス制御リスト) を定義することを検討してください。包括的なマッピング テーブルは、元のテーブル内のどのデータ行が特定のユーザーまたはグループからアクセスできるかをエンコードします。マッピング テーブルは、直接結合を通じてファクト テーブルと簡単に統合できるため便利です。
この方法論は、カスタム要件を含む多くのユースケースに対応します。例としては、次のようなものがあります。
- ログインしたユーザーに基づいて制限を課し、特定のユーザーグループに対して異なるルールに対応する。
- 組織構造など、多様なルールセットを必要とする複雑な階層を作成します。
- 外部ソースシステムからの複雑なセキュリティモデルを複製する。
マッピング テーブルを採用することで、これらの困難なシナリオを実現し、堅牢な行レベルおよび列レベルのセキュリティ実装を確保できます。
マッピングテーブルの例
マッピングテーブルを使用して、現在のユーザーがリストにあるかどうかを確認します。
USE CATALOG main;
新しいマッピングテーブルを作成します。
DROP TABLE IF EXISTS valid_users;
CREATE TABLE valid_users(username string);
INSERT INTO valid_users
VALUES
('fred@databricks.com'),
('barney@databricks.com');
新しいフィルターを作成します。
呼び出し元として実行されるユーザーコンテキストをチェックする関数(たとえば、 CURRENT_USER 関数や IS_ACCOUNT_GROUP_MEMBER 関数)を除き、すべてのフィルターは定義者の権限で実行されます。
この例では、関数は現在のユーザーが valid_users テーブルにいるかどうかをチェックします。ユーザーが見つかった場合、関数は true を返します。
DROP FUNCTION IF EXISTS row_filter;
CREATE FUNCTION row_filter()
RETURN EXISTS(
SELECT 1 FROM valid_users v
WHERE v.username = CURRENT_USER()
);
次の例では、テーブルの作成時に行フィルターを適用します。後で ALTER TABLE ステートメントを使用してフィルターを追加することもできます。未指定の列にフィルターを適用する場合は、 ON () 構文を使用します。特定の列には、 ON (column);を使用します。詳細については、「 パラメーター」を参照してください。
DROP TABLE IF EXISTS data_table;
CREATE TABLE data_table
(x INT, y INT, z INT)
WITH ROW FILTER row_filter ON ();
INSERT INTO data_table VALUES
(1, 2, 3),
(4, 5, 6),
(7, 8, 9);
テーブルからデータを選択します。このデータは、ユーザーがvalid_usersテーブルにいる場合にのみ返されます。
SELECT * FROM data_table;
列の値に関係なく、常にテーブルのすべての行を表示できるアカウントを含むマッピングテーブルを作成します。
CREATE TABLE valid_accounts(account string);
INSERT INTO valid_accounts
VALUES
('admin'),
('cstaff');
次に、行内のすべての列の値が 5 未満の場合、または呼び出し元のユーザーが上記のマッピング テーブルのメンバーであるかどうかに true を返す SQL UDF を作成します。
CREATE FUNCTION row_filter_small_values (x INT, y INT, z INT)
RETURN (x < 5 AND y < 5 AND z < 5)
OR EXISTS(
SELECT 1 FROM valid_accounts v
WHERE IS_ACCOUNT_GROUP_MEMBER(v.account));
最後に、SQL UDFを行フィルターとしてテーブルに適用します。
ALTER TABLE data_table SET ROW FILTER row_filter_small_values ON (x, y, z);