
Snowflake の Data Classification で機密データの自動分類からカスタム分類まで試してみた
かわばたです。
DWHにデータが集まるほど、「どのカラムに機密データが含まれているか」を把握・管理する負荷が増えていきます。カラム数が数百・数千に達すると手動での棚卸しは現実的ではなく、新しいテーブルが追加されるたびに分類漏れのリスクも高まります。
Snowflake の Sensitive Data Classification(以下、Data Classification) は、テーブル内の機密データを自動で検出・分類し、タグとして付与できる機能です。ビルトインの多数のネイティブカテゴリに加え、正規表現ベースのカスタム分類で組織固有の識別子にも対応できます。
手動分類の限界をどこまで Data Classification で解消できるか、自動分類の基本からカスタム分類、除外設定、結果確認まで一通り検証してみたので、手順と結果をまとめます。
背景・課題
データガバナンスの観点では、機密データ(個人名、メールアドレス、クレジットカード番号など)がどのカラムに格納されているかを把握し、適切なアクセス制御やマスキングを施す必要があります。
しかし、実運用では以下のような課題が生じます。
- カラム数の増大: テーブルやスキーマが増えるたびに、全カラムを手動で棚卸しして機密データを特定するのは膨大な工数がかかる
- 分類漏れのリスク: 新規テーブル追加時やスキーマ変更時に分類が追いつかず、機密データが未管理のまま残る
- 組織固有の識別子: 社員番号や会員コードなど、汎用的な個人情報カテゴリに該当しないが組織内では機密として扱うべきデータがある
- 継続的な更新: 一度分類しても、データ構造の変化に合わせて定期的に再分類が必要
これらの課題に対して、Snowflake の Data Classification 機能を使って自動化・省力化がどこまでできるかを検証します。
技術的アプローチ
Snowflake Data Classification とは
Snowflake Data Classification は、テーブル内のカラムを分析し、機密データを自動で検出・分類する機能です。分類結果はタグとしてカラムに付与でき、マスキングポリシーやアクセス制御と連携してデータガバナンスを強化できます。
分類は 2つの軸 で行われます。
| 軸 | 説明 | 例 |
|---|---|---|
| セマンティックカテゴリ | データの属性の種類(何のデータか) | NAME、EMAIL、PHONE_NUMBER、NATIONAL_IDENTIFIER、PAYMENT_CARD |
| プライバシーカテゴリ | データの機密度レベル | IDENTIFIER(識別子)、QUASI_IDENTIFIER(準識別子)、SENSITIVE(センシティブ情報) |
例えば、メールアドレスのカラムは「セマンティックカテゴリ: EMAIL」「プライバシーカテゴリ: IDENTIFIER」と分類されます。
ネイティブ分類とカスタム分類の使い分け
Data Classification には2つのアプローチがあります。
| アプローチ | 概要 | ユースケース |
|---|---|---|
| ネイティブ分類 | Snowflake がビルトインで提供する多数のセマンティックカテゴリ(NAME、EMAIL、NATIONAL_IDENTIFIER 等)で自動検出 | 一般的な個人情報(PII)の検出 |
| カスタム分類 | CUSTOM_CLASSIFIER クラスで正規表現ベースの独自カテゴリを定義 |
社員番号、会員コード等の組織固有識別子の検出 |
ネイティブカテゴリでカバーできない組織固有の識別子は、カスタム分類で正規表現パターンを定義して対応します。
自動分類の仕組み
自動分類の処理フローを整理します。
- 分類プロファイルの作成: 分類条件(再分類間隔、タグ自動付与の有無、ビュー対象の有無等)を定義
- データベース/スキーマへの適用:
ALTER DATABASE SET CLASSIFICATION_PROFILEでプロファイルを紐付け - サーバーレスコンピュートで分類実行: ランダムに選択された最大10,000行のサンプルを分析
- 結果のタグ付与:
auto_tag: trueの場合、SEMANTIC_CATEGORY/PRIVACY_CATEGORYのシステムタグを自動でカラムに付与
分類プロファイルを設定すると、Snowflake のサーバーレスコンピュートが自動で分類処理を実行します。分類プロファイルを設定してから自動分類プロセスが開始されるまで約1時間の遅延があります。テーブル数やデータ量によっては、結果が確認できるまでさらに時間がかかる場合があります。
制限事項
- Enterprise Edition 以上が必須(Standard Edition では利用不可)
- 非対応データ型: BINARY、DECFLOAT、GEOGRAPHY、UUID、VECTOR のカラムは分類スキップ
- 10,000列を超えるテーブルは自動分類不可
- カラム名が255文字超過または
$を含む場合は対象外 - 分類プロファイルは最大1,000データベース / 10,000スキーマに設定可能
- 分類はランダムに選択された最大10,000行のサンプルに基づく
- 手動で設定済みのタグと自動タグが競合する場合はエラーが発生
- カスタム分類の定義はプロファイルに値としてコピー保存される(参照ではない)。分類を変更した場合は
SET_CUSTOM_CLASSIFIERSで再設定が必要 - ビューの分類はテーブル直接分類より高コスト(デフォルトではビューは分類対象外)
コスト
- Data Classification はサーバーレスコンピュートリソースを使用するため、クレジットを消費する
- ビューの分類は基礎テーブルを直接分類するより高コスト。マテリアライズドビューは追加コストなし
- コスト確認は
METERING_HISTORYビューでservice_type = 'SENSITIVE_DATA_CLASSIFICATION'をフィルタする - 自動分類はサーバーレスコンピュートを使用する(カスタム分類を組み込んだ自動分類も同様)
前提条件
- Snowflake: AWS 東京リージョン、Enterprise Edition
- 本機能のステータス: GA(一般提供)
- 必要な権限: ACCOUNTADMIN ロール(初期設定時)。本番環境ではカスタムロール推奨
事前準備
検証用データベース・スキーマの作成
分類プロファイルを格納するガバナンス用データベースと、検証データ用データベースを作成します。
検証用データベース・スキーマ
USE ROLE ACCOUNTADMIN;
-- ガバナンス用データベース(分類プロファイル・カスタム分類を格納)
CREATE DATABASE IF NOT EXISTS governance_db;
CREATE SCHEMA IF NOT EXISTS governance_db.classify_sch;
-- 検証データ用データベース
CREATE DATABASE IF NOT EXISTS classify_test_db;
CREATE SCHEMA IF NOT EXISTS classify_test_db.sch;
検証用テーブルの作成とサンプルデータ投入
テーブル1: customers(ネイティブカテゴリ検証用)
さまざまな種類の個人情報カラムを含むテーブルを作成します。ネイティブセマンティックカテゴリの検出精度を確認するため、一般的な PII(名前、メール、電話番号、SSN 等)に加え、日本語氏名のカラムも含めます。
customers(ネイティブカテゴリ検証用)
USE SCHEMA classify_test_db.sch;
CREATE OR REPLACE TABLE customers (
customer_id NUMBER COMMENT '顧客ID(一意の識別子)',
first_name VARCHAR COMMENT '英語名',
last_name VARCHAR COMMENT '英語姓',
full_name_jp VARCHAR COMMENT '日本語氏名',
email VARCHAR COMMENT 'メールアドレス',
phone_number VARCHAR COMMENT '電話番号',
us_ssn VARCHAR COMMENT '米国社会保障番号',
date_of_birth DATE COMMENT '生年月日',
gender VARCHAR COMMENT '性別',
street_address VARCHAR COMMENT '住所',
zip_code VARCHAR COMMENT '郵便番号',
ip_address VARCHAR COMMENT 'IPアドレス',
credit_card_number VARCHAR COMMENT 'クレジットカード番号',
salary NUMBER(10, 2) COMMENT '年収',
notes VARCHAR COMMENT '備考(機密情報なし)'
);
-- 検証用ダミーデータの投入(20レコード)
INSERT INTO customers VALUES
(1, 'John', 'Smith', '田中太郎', 'john.smith@example.com', '+1-555-0101', '123-45-6789', '1985-03-15', 'Male', '123 Main St, New York, NY', '10001', '192.168.1.1', '4111111111111111', 85000.00, 'VIP customer'),
(2, 'Jane', 'Doe', '佐藤花子', 'jane.doe@example.com', '+1-555-0102', '234-56-7890', '1990-07-22', 'Female', '456 Oak Ave, Los Angeles, CA', '90001', '10.0.0.1', '5500000000000004', 72000.00, 'Regular customer'),
(3, 'Michael', 'Johnson', '鈴木一郎', 'michael.j@example.com', '+1-555-0103', '345-67-8901', '1978-11-03', 'Male', '789 Pine Rd, Chicago, IL', '60601', '172.16.0.1', '340000000000009', 95000.00, NULL),
(4, 'Emily', 'Williams', '高橋美咲', 'emily.w@example.com', '+1-555-0104', '456-78-9012', '1992-01-30', 'Female', '321 Elm Blvd, Houston, TX', '77001', '192.168.0.100', '4222222222222', 68000.00, 'New account'),
(5, 'David', 'Brown', '伊藤健太', 'david.b@example.com', '+1-555-0105', '567-89-0123', '1988-05-18', 'Male', '654 Maple Dr, Phoenix, AZ', '85001', '10.1.1.1', '5105105105105100', 91000.00, NULL),
(6, 'Sarah', 'Davis', '渡辺直美', 'sarah.d@example.com', '+1-555-0106', '678-90-1234', '1995-09-07', 'Female', '987 Cedar Ln, Philadelphia, PA', '19101', '192.168.2.50', '4000056655665556', 63000.00, 'Preferred customer'),
(7, 'Robert', 'Miller', '山本学', 'robert.m@example.com', '+1-555-0107', '789-01-2345', '1982-12-25', 'Male', '147 Birch Way, San Antonio, TX', '78201', '172.16.1.10', '6011111111111117', 78000.00, NULL),
(8, 'Lisa', 'Wilson', '中村陽子', 'lisa.w@example.com', '+1-555-0108', '890-12-3456', '1993-04-11', 'Female', '258 Walnut St, San Diego, CA', '92101', '10.10.10.10', '3530111333300000', 82000.00, 'Corporate account'),
(9, 'James', 'Moore', '小林誠', 'james.m@example.com', '+1-555-0109', '901-23-4567', '1975-08-19', 'Male', '369 Spruce Ave, Dallas, TX', '75201', '192.168.5.1', '4111111111111111', 105000.00, NULL),
(10, 'Amanda', 'Taylor', '加藤裕二', 'amanda.t@example.com', '+1-555-0110', '012-34-5678', '1987-02-14', 'Female', '741 Ash Ct, San Jose, CA', '95101', '10.0.1.1', '5500000000000004', 74000.00, 'Trial period'),
(11, 'Christopher', 'Anderson', '吉田真理', 'chris.a@example.com', '+1-555-0111', '111-22-3333', '1991-06-28', 'Male', '852 Hickory Pl, Austin, TX', '73301', '172.16.2.20', '340000000000009', 88000.00, NULL),
(12, 'Jennifer', 'Thomas', '松本大輔', 'jennifer.t@example.com', '+1-555-0112', '222-33-4444', '1984-10-05', 'Female', '963 Poplar Dr, Jacksonville, FL', '32099', '192.168.3.75', '4222222222222', 71000.00, 'Seasonal buyer'),
(13, 'Daniel', 'Jackson', '井上さくら', 'daniel.j@example.com', '+1-555-0113', '333-44-5555', '1996-03-20', 'Male', '159 Willow Rd, Fort Worth, TX', '76101', '10.2.2.2', '5105105105105100', 66000.00, NULL),
(14, 'Jessica', 'White', '木村拓也', 'jessica.w@example.com', '+1-555-0114', '444-55-6666', '1989-07-08', 'Female', '267 Sycamore Blvd, Columbus, OH', '43085', '192.168.4.200', '4000056655665556', 93000.00, 'Loyalty member'),
(15, 'Matthew', 'Harris', '林美穂', 'matthew.h@example.com', '+1-555-0115', '555-66-7777', '1980-11-15', 'Male', '378 Cypress St, Charlotte, NC', '28201', '172.16.3.30', '6011111111111117', 99000.00, NULL),
(16, 'Ashley', 'Martin', '清水翔太', 'ashley.m@example.com', '+1-555-0116', '666-77-8888', '1994-01-22', 'Female', '489 Magnolia Ave, Indianapolis, IN', '46201', '10.3.3.3', '3530111333300000', 70000.00, 'Student discount'),
(17, 'Andrew', 'Garcia', '斎藤恵', 'andrew.g@example.com', '+1-555-0117', '777-88-9999', '1986-05-30', 'Male', '591 Cherry Ln, San Francisco, CA', '94101', '192.168.6.1', '4111111111111111', 110000.00, NULL),
(18, 'Megan', 'Martinez', '前田健一', 'megan.m@example.com', '+1-555-0118', '888-99-0000', '1997-09-12', 'Female', '602 Peach Dr, Seattle, WA', '98101', '10.4.4.4', '5500000000000004', 67000.00, 'Referral program'),
(19, 'Joshua', 'Robinson', '藤井優子', 'joshua.r@example.com', '+1-555-0119', '999-00-1111', '1983-12-01', 'Male', '713 Plum Way, Denver, CO', '80201', '172.16.4.40', '340000000000009', 86000.00, NULL),
(20, 'Lauren', 'Clark', '岡田龍一', 'lauren.c@example.com', '+1-555-0120', '000-11-2222', '1990-04-25', 'Female', '824 Olive Ct, Nashville, TN', '37201', '192.168.7.100', '4222222222222', 79000.00, 'Premium member');
テーブルの内容を SELECT して、20件のデータが正しく入っていることを確認しておきます。
SELECT * FROM customers ORDER BY customer_id LIMIT 5;

テーブル2: custom_ids(カスタム分類検証用)
組織固有の識別子パターンを持つカラムを含むテーブルを作成します。カスタム分類の正規表現マッチングを検証します。
custom_ids(カスタム分類検証用)
CREATE OR REPLACE TABLE custom_ids (
record_id NUMBER COMMENT 'レコードID',
employee_id VARCHAR COMMENT '社員番号(EMP-######形式)',
member_code VARCHAR COMMENT '会員コード(MBR-########形式)',
project_id VARCHAR COMMENT 'プロジェクトID(PRJ-####形式)',
department_code VARCHAR COMMENT '部署コード(3桁英字)'
);
-- 検証用ダミーデータの投入(20レコード)
INSERT INTO custom_ids VALUES
(1, 'EMP-100001', 'MBR-20240001', 'PRJ-0001', 'ENG'),
(2, 'EMP-100002', 'MBR-20240002', 'PRJ-0002', 'MKT'),
(3, 'EMP-100003', 'MBR-20240003', 'PRJ-0003', 'HRD'),
(4, 'EMP-100004', 'MBR-20240004', 'PRJ-0001', 'FIN'),
(5, 'EMP-100005', 'MBR-20240005', 'PRJ-0004', 'ENG'),
(6, 'EMP-100006', 'MBR-20240006', 'PRJ-0002', 'SAL'),
(7, 'EMP-100007', 'MBR-20240007', 'PRJ-0005', 'MKT'),
(8, 'EMP-100008', 'MBR-20240008', 'PRJ-0003', 'ENG'),
(9, 'EMP-100009', 'MBR-20240009', 'PRJ-0006', 'HRD'),
(10, 'EMP-100010', 'MBR-20240010', 'PRJ-0004', 'FIN'),
(11, 'EMP-100011', 'MBR-20240011', 'PRJ-0007', 'SAL'),
(12, 'EMP-100012', 'MBR-20240012', 'PRJ-0005', 'ENG'),
(13, 'EMP-100013', 'MBR-20240013', 'PRJ-0008', 'MKT'),
(14, 'EMP-100014', 'MBR-20240014', 'PRJ-0006', 'HRD'),
(15, 'EMP-100015', 'MBR-20240015', 'PRJ-0009', 'FIN'),
(16, 'EMP-100016', 'MBR-20240016', 'PRJ-0007', 'ENG'),
(17, 'EMP-100017', 'MBR-20240017', 'PRJ-0010', 'SAL'),
(18, 'EMP-100018', 'MBR-20240018', 'PRJ-0008', 'MKT'),
(19, 'EMP-100019', 'MBR-20240019', 'PRJ-0009', 'HRD'),
(20, 'EMP-100020', 'MBR-20240020', 'PRJ-0010', 'FIN');

テーブル3: exclusion_test(除外設定検証用)
分類対象からの除外設定を検証するためのテーブルです。
exclusion_test(除外設定検証用)
CREATE OR REPLACE TABLE exclusion_test (
id NUMBER COMMENT 'レコードID',
email VARCHAR COMMENT 'メールアドレス(除外対象カラムとして使用)',
phone VARCHAR COMMENT '電話番号(分類対象として残す)',
full_name VARCHAR COMMENT '氏名(分類対象として残す)'
);
INSERT INTO exclusion_test VALUES
(1, 'alice@example.com', '+1-555-0201', 'Alice Johnson'),
(2, 'bob@example.com', '+1-555-0202', 'Bob Williams'),
(3, 'carol@example.com', '+1-555-0203', 'Carol Davis'),
(4, 'dave@example.com', '+1-555-0204', 'Dave Miller'),
(5, 'eve@example.com', '+1-555-0205', 'Eve Wilson'),
(6, 'frank@example.com', '+1-555-0206', 'Frank Moore'),
(7, 'grace@example.com', '+1-555-0207', 'Grace Taylor'),
(8, 'henry@example.com', '+1-555-0208', 'Henry Anderson'),
(9, 'iris@example.com', '+1-555-0209', 'Iris Thomas'),
(10, 'jack@example.com', '+1-555-0210', 'Jack Jackson');

権限の設定
本検証では ACCOUNTADMIN ロールで実施しますが、本番環境で推奨されるカスタムロールの設定例も示します。
権限
USE ROLE ACCOUNTADMIN;
-- カスタムロールの作成(本番環境向け参考)
CREATE ROLE IF NOT EXISTS classification_admin_role;
-- 分類関連のデータベースロールを付与
GRANT DATABASE ROLE SNOWFLAKE.CLASSIFICATION_ADMIN TO ROLE classification_admin_role;
GRANT DATABASE ROLE SNOWFLAKE.GOVERNANCE_ADMIN TO ROLE classification_admin_role;
GRANT DATABASE ROLE SNOWFLAKE.GOVERNANCE_VIEWER TO ROLE classification_admin_role;
-- 必要な権限を付与
GRANT EXECUTE AUTO CLASSIFICATION ON ACCOUNT TO ROLE classification_admin_role;
GRANT APPLY TAG ON ACCOUNT TO ROLE classification_admin_role;
GRANT USAGE ON DATABASE governance_db TO ROLE classification_admin_role;
GRANT USAGE ON SCHEMA governance_db.classify_sch TO ROLE classification_admin_role;
GRANT CREATE SNOWFLAKE.DATA_PRIVACY.CLASSIFICATION_PROFILE ON SCHEMA governance_db.classify_sch TO ROLE classification_admin_role;
GRANT CREATE SNOWFLAKE.DATA_PRIVACY.CUSTOM_CLASSIFIER ON SCHEMA governance_db.classify_sch TO ROLE classification_admin_role;
GRANT CREATE TAG ON SCHEMA governance_db.classify_sch TO ROLE classification_admin_role;
GRANT USAGE ON DATABASE classify_test_db TO ROLE classification_admin_role;
GRANT USAGE ON SCHEMA classify_test_db.sch TO ROLE classification_admin_role;
GRANT SELECT ON ALL TABLES IN SCHEMA classify_test_db.sch TO ROLE classification_admin_role;
GRANT USAGE ON WAREHOUSE <your_warehouse_name> TO ROLE classification_admin_role;
試してみた
自動分類を設定して実行する
分類プロファイルの作成
まず、ガバナンス用スキーマに分類プロファイルを作成します。minimum_object_age_for_classification_days を 0 に設定して、作成直後のテーブルも分類対象にします。
USE ROLE ACCOUNTADMIN;
CREATE OR REPLACE SNOWFLAKE.DATA_PRIVACY.CLASSIFICATION_PROFILE
governance_db.classify_sch.my_classification_profile({
'minimum_object_age_for_classification_days': 0,
'maximum_classification_validity_days': 30,
'auto_tag': true,
'classify_views': false
});

作成したプロファイルの設定内容を DESCRIBE メソッドで確認します。
SELECT governance_db.classify_sch.my_classification_profile!DESCRIBE();

データベースへの適用
検証用データベースに分類プロファイルを設定します。
ALTER DATABASE classify_test_db
SET CLASSIFICATION_PROFILE = 'governance_db.classify_sch.my_classification_profile';

プロファイルの設定後、自動分類が開始されるまで最大1時間の遅延があります。分類の進行状況は後述の方法で確認できます。
カテゴリの分類結果を確認する
分類結果およびタグ付け結果の確認には、主に以下の方法があります。
SYSTEM$GET_CLASSIFICATION_RESULT(テーブル単位)
SELECT SYSTEM$GET_CLASSIFICATION_RESULT('classify_test_db.sch.customers');

JSON 形式で各カラムの分類結果が返ります。各カラムについて、検出されたセマンティックカテゴリ、プライバシーカテゴリ、信頼度(HIGH / MEDIUM / LOW)、coverage(サンプル値のうち該当カテゴリに一致した割合)を確認できます。
以下のようなカラムごとの分類結果が期待されます。
| カラム | 実際のセマンティックカテゴリ | 補足 | 実際のプライバシーカテゴリ |
|---|---|---|---|
| first_name / last_name | NAME | 英語氏名は検出された | IDENTIFIER |
| full_name_jp | ―(分類なし) | 今回の検証では日本語氏名は NAME として検出されなかった | ― |
| IDENTIFIER | |||
| phone_number | ―(分類なし) | 今回の検証データでは PHONE_NUMBER として検出されなかった | ― |
| us_ssn | NATIONAL_IDENTIFIER | サブカテゴリに US_SSN | IDENTIFIER |
| date_of_birth | DATE_OF_BIRTH | QUASI_IDENTIFIER | |
| gender | GENDER | QUASI_IDENTIFIER | |
| street_address | STREET_ADDRESS | サブカテゴリに US_STREET_ADDRESS / CA_STREET_ADDRESS | IDENTIFIER |
| zip_code | POSTAL_CODE | サブカテゴリに US_POSTAL_CODE | QUASI_IDENTIFIER |
| ip_address | IP_ADDRESS | IDENTIFIER | |
| credit_card_number | PAYMENT_CARD | IDENTIFIER | |
| salary | SALARY | SENSITIVE | |
| customer_id / notes | ―(分類なし) | ― |
分類結果
{
"classification_profile_config": {
"classification_profile_name": "GOVERNANCE_DB.CLASSIFY_SCH.MY_CLASSIFICATION_PROFILE"
},
"classification_result": {
"CREDIT_CARD_NUMBER": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "IDENTIFIER",
"semantic_category": "PAYMENT_CARD",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "PAYMENT_CARD"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"CUSTOMER_ID": {
"alternates": []
},
"DATE_OF_BIRTH": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "QUASI_IDENTIFIER",
"semantic_category": "DATE_OF_BIRTH",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "DATE_OF_BIRTH"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "QUASI_IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"EMAIL": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "IDENTIFIER",
"semantic_category": "EMAIL",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "EMAIL"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"FIRST_NAME": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "IDENTIFIER",
"semantic_category": "NAME",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "NAME"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"FULL_NAME_JP": {
"alternates": []
},
"GENDER": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "QUASI_IDENTIFIER",
"semantic_category": "GENDER",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "GENDER"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "QUASI_IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"IP_ADDRESS": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "IDENTIFIER",
"semantic_category": "IP_ADDRESS",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "IP_ADDRESS"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"LAST_NAME": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "IDENTIFIER",
"semantic_category": "NAME",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "NAME"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"NOTES": {
"alternates": []
},
"PHONE_NUMBER": {
"alternates": []
},
"SALARY": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [],
"privacy_category": "SENSITIVE",
"semantic_category": "SALARY",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "SALARY"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "SENSITIVE"
}
]
},
"valid_value_ratio": 1
},
"STREET_ADDRESS": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 1,
"details": [
{
"coverage": 1,
"semantic_category": "US_STREET_ADDRESS"
},
{
"coverage": 1,
"semantic_category": "CA_STREET_ADDRESS"
}
],
"privacy_category": "IDENTIFIER",
"semantic_category": "STREET_ADDRESS",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "STREET_ADDRESS"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"US_SSN": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 0.75,
"details": [
{
"coverage": 0.75,
"semantic_category": "US_SSN"
}
],
"privacy_category": "IDENTIFIER",
"semantic_category": "NATIONAL_IDENTIFIER",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "NATIONAL_IDENTIFIER"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "IDENTIFIER"
}
]
},
"valid_value_ratio": 1
},
"ZIP_CODE": {
"alternates": [],
"recommendation": {
"confidence": "HIGH",
"coverage": 0.95,
"details": [
{
"coverage": 0.95,
"semantic_category": "US_POSTAL_CODE"
}
],
"privacy_category": "QUASI_IDENTIFIER",
"semantic_category": "POSTAL_CODE",
"tags": [
{
"tag_applied": true,
"tag_name": "snowflake.core.semantic_category",
"tag_value": "POSTAL_CODE"
},
{
"tag_applied": true,
"tag_name": "snowflake.core.privacy_category",
"tag_value": "QUASI_IDENTIFIER"
}
]
},
"valid_value_ratio": 1
}
}
}
Trust Center UI
Snowsight の Trust Center UI からも分類結果を確認できます。
- Snowsight にサインイン
- ナビゲーションメニューで
Governance & security→Trust Centerを選択 Data Securityタブを選択Dashboardタブで分類結果のサマリーを確認Sensitive objectsタブで個別のテーブル・カラムの分類結果を確認


自動タグ付けの確認
auto_tag: true を設定しているため、分類結果がシステムタグとしてカラムに付与されているはずです。TAG_REFERENCES ビューで確認します。
SELECT
object_database,
object_schema,
object_name,
column_name,
tag_name,
tag_value
FROM SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES
WHERE tag_name IN ('SEMANTIC_CATEGORY', 'PRIVACY_CATEGORY')
AND object_database = 'CLASSIFY_TEST_DB'
ORDER BY object_name, column_name, tag_name;

特定のカラムに付与されたタグ値を直接確認することもできます。
SELECT SYSTEM$GET_TAG(
'SNOWFLAKE.CORE.SEMANTIC_CATEGORY',
'classify_test_db.sch.customers.email',
'COLUMN'
);

カスタム分類で独自カテゴリを追加する
ネイティブカテゴリでは検出できない組織固有の識別子を、カスタム分類で定義します。
カスタム分類の作成
CREATE OR REPLACE SNOWFLAKE.DATA_PRIVACY.CUSTOM_CLASSIFIER
governance_db.classify_sch.internal_ids();

正規表現ルールの追加
社員番号(EMP-######)と会員コード(MBR-########)のパターンを定義します。
-- 社員番号のパターンを追加
CALL governance_db.classify_sch.internal_ids!ADD_REGEX(
SEMANTIC_CATEGORY => 'EMPLOYEE_ID',
PRIVACY_CATEGORY => 'IDENTIFIER',
VALUE_REGEX => '^EMP-[0-9]{6}$',
COL_NAME_REGEX => '.*EMP.*ID.*',
DESCRIPTION => '社員番号(EMP-######形式)を識別するカスタムカテゴリ',
THRESHOLD => 0.8
);
-- 会員コードのパターンを追加
CALL governance_db.classify_sch.internal_ids!ADD_REGEX(
SEMANTIC_CATEGORY => 'MEMBER_CODE',
PRIVACY_CATEGORY => 'IDENTIFIER',
VALUE_REGEX => '^MBR-[0-9]{8}$',
COL_NAME_REGEX => '.*MEMBER.*',
DESCRIPTION => '会員コード(MBR-########形式)を識別するカスタムカテゴリ',
THRESHOLD => 0.8
);

登録したルールの一覧を確認します。
SELECT governance_db.classify_sch.internal_ids!LIST();

2つのカスタムカテゴリ(EMPLOYEE_ID と MEMBER_CODE)が登録されていれば問題ありません。
カスタム分類を分類プロファイルに組み込む
作成したカスタム分類を既存の分類プロファイルに設定します。SET_CUSTOM_CLASSIFIERS にはカスタム分類名をキー、その LIST() 結果を値とする OBJECT を渡します。
CALL governance_db.classify_sch.my_classification_profile!SET_CUSTOM_CLASSIFIERS(
{
'internal_ids': governance_db.classify_sch.internal_ids!LIST()
}
);

カスタム分類の結果確認
カスタム分類の組み込み後、再分類が完了するのを待ってから結果を確認します。
SELECT SYSTEM$GET_CLASSIFICATION_RESULT('classify_test_db.sch.custom_ids');

以下のような結果が期待されます。
| カラム | 期待されるカスタムカテゴリ | 期待されるプライバシーカテゴリ |
|---|---|---|
| employee_id | EMPLOYEE_ID | IDENTIFIER |
| member_code | MEMBER_CODE | IDENTIFIER |
| project_id | ―(パターン未定義) | ― |
| department_code | ―(パターン未定義) | ― |
employee_id と member_code がカスタムカテゴリで正しく検出されていれば問題ありません。
ユーザー定義タグと自動タグ付けを設定する
システムタグ(SEMANTIC_CATEGORY / PRIVACY_CATEGORY)に加えて、ユーザー定義のタグを自動的に付与する設定を試します。
ユーザー定義タグの作成
CREATE TAG IF NOT EXISTS governance_db.classify_sch.pii_level;
タグマップの設定
分類結果に基づいて、ユーザー定義タグを自動付与するマッピングルールを設定します。ここでは、NAME カテゴリのカラムに pii_level = 'high' タグを付与する例を示します。
CALL governance_db.classify_sch.my_classification_profile!SET_TAG_MAP({
'column_tag_map': [{
'tag_name': 'governance_db.classify_sch.pii_level',
'tag_value': 'high',
'semantic_categories': ['NAME']
}, {
'tag_name': 'governance_db.classify_sch.pii_level',
'tag_value': 'high',
'semantic_categories': ['EMAIL', 'NATIONAL_IDENTIFIER', 'PAYMENT_CARD']
}, {
'tag_name': 'governance_db.classify_sch.pii_level',
'tag_value': 'medium',
'semantic_categories': ['PHONE_NUMBER', 'IP_ADDRESS', 'DATE_OF_BIRTH']
}, {
'tag_name': 'governance_db.classify_sch.pii_level',
'tag_value': 'high',
'semantic_categories': ['EMPLOYEE_ID', 'MEMBER_CODE']
}]
});
タグ付け結果の確認
再分類が完了した後、ユーザー定義タグが付与されていることを確認します。
SELECT *
FROM TABLE(CLASSIFY_TEST_DB.INFORMATION_SCHEMA.TAG_REFERENCES_ALL_COLUMNS('sch.custom_ids', 'TABLE'))
WHERE tag_name = 'PII_LEVEL'
ORDER BY column_name;

分類対象から除外する
特定のカラムやテーブルを分類対象から除外する設定を試します。
SKIP_SENSITIVE_DATA_CLASSIFICATION タグの付与
exclusion_test テーブルの email カラムを分類対象から除外します。
ALTER TABLE classify_test_db.sch.exclusion_test ALTER COLUMN email
SET TAG SNOWFLAKE.CORE.SKIP_SENSITIVE_DATA_CLASSIFICATION = 'TRUE';
除外設定の有効化
分類プロファイルでタグベースの除外機能を有効化します。既存のプロファイルに対して SET_ENABLE_TAG_BASED_SENSITIVE_DATA_EXCLUSION メソッドを使用します。
CALL governance_db.classify_sch.my_classification_profile!SET_ENABLE_TAG_BASED_SENSITIVE_DATA_EXCLUSION(TRUE);
除外結果の確認
再分類後に exclusion_test テーブルの分類結果を確認し、email カラムが分類から除外されていることを確認します。
SELECT SYSTEM$GET_CLASSIFICATION_RESULT('classify_test_db.sch.exclusion_test');

email カラムの分類結果が空の値として返り、full_nameカラムは引き続き分類されていれば問題ありません。
最後に
Snowflake の Data Classification 機能を使い、自動分類の基本設定からカスタム分類の作成、除外設定、結果確認まで一通り検証しました。
自動分類は分類プロファイルを1つ作成してデータベースに適用するだけで、ネイティブカテゴリ(NAME、EMAIL、NATIONAL_IDENTIFIER 等)の検出が自動で行われる点が便利です。カラム数の多いテーブルが増えても、手動での棚卸し工数を大幅に削減できることがメリットと考えています。
カスタム分類は、正規表現ベースで組織固有の識別子(社員番号、会員コードなど)を定義できるため、ネイティブカテゴリだけではカバーしきれないユースケースにも対応できるのは良い点だと思いました。
この記事が何かの参考になれば幸いです!
参考







