[新機能]Snowflake の Restricted Session Scope が一般提供になったので CoCo 利用時だけ実効権限を狭めてみた

[新機能]Snowflake の Restricted Session Scope が一般提供になったので CoCo 利用時だけ実効権限を狭めてみた

Snowflake の Restricted Session Scope(RSS)が一般提供となりました。実務で想定される 4 つのユースケースで CoCo in Snowsight から検証します。
2026.09.04

かわばたです。

2026年9月3日にRestricted Session Scope(RSS)が一般提供となりました。
実務で想定される 4 つのユースケースで CoCo in Snowsight から検証します。

https://docs.snowflake.com/en/user-guide/restricted-session-scope

【追記】
Snowflake Community Awards の「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
詳細は下記よりご確認ください。

https://dev.classmethod.jp/articles/snowflake-community-awards-finalist-activities-review/

先に結論

  • RSS は、既存の RBAC を一切変えずに「エージェントが有効なセッションだけ」実効権限を狭める仕組みです。エージェントが実行できるのは「RBAC で許可され、かつ RSS でも許可された操作」だけで、権限が増えることはありません
  • 本番データベースへの書き込みを RSS の許可範囲から外し、特権ロールへの切り替えを禁止することで、エージェントセッションをユーザー単位で読み取り専用に近い形に制限できます
  • data write はテーブルのデータ書き込みだけを対象とし、CREATE TABLE などの DDL は含みません。テーブル作成まで許すなら object management を追加します
  • 「触れる範囲」を絞る RSS と、「触れた後に見える値」を絞る agent-aware マスキングは補完関係にあり、同じセッションで重ねられます
  • 今回の検証では、同じユーザー・同じロールでも、CoCo のエージェントセッションでは本番テーブルへの INSERT、DDL、ロール切り替えが拒否され、ワークシートでは RBAC の権限どおりに実行できることを確認しました

全体像

RSS が守備範囲とするのは、Snowflake のガバナンス機能の中でも「エージェントが動いている間の、触れる範囲」です。

仕組み 制御する軸 単位
RBAC 誰が何をできるか ロール
Feature Policy どのオブジェクトを作成できるか DB などのコンテナ
マスキング / 射影 / 集計ポリシー 触れたデータのどの値が見えるか 列・テーブル
RSS エージェント時に、どの操作・どの範囲に触れられるか セッション(エージェント有効時)

本記事では、この位置づけを踏まえて次の流れで進めます。

  1. これまでの課題
  2. RSS とは
  3. 検証
  4. おわりに

これまでの課題

Snowflake のアクセス制御は RBAC が基本です。ロールに権限を付け、ユーザーにロールを付ける。この仕組みは「誰が」の単位で権限を決めるため、同じユーザーが人としてワークシートから操作しても、エージェント経由で操作しても、使える権限は変わりません。

人が使う前提で設計した分析ロールに INSERT や CREATE TABLE が付いていれば、CoCo も同じ操作ができます。さらに CoCo はユーザーのデフォルトロールでセッションを始めますが、会話の流れで「ロールを切り替えて」と指示すれば USE ROLE を実行できます。確認ダイアログは出るものの、事前に禁止する手段がありませんでした。

IS_AGENT_ACTIVATED を条件にしたマスキングポリシーで「見える値」を絞る方法もありますが、これは「触れた後」の話です。

下記で検証しているので、詳細が気になる方はご確認ください。
https://dev.classmethod.jp/articles/snowflake-is-agent-activated-coco-pii-masking/

「触れる範囲」そのものを、エージェントのときだけ狭めることができるのがRSSです。

RSS とは

RSS は、エージェントに対する権限の上限(privilege ceiling)を定義する仕組みです。自作の RSS は YAML で定義でき、Snowflake 定義済みのスコープを名前で参照することもできます。ユーザーの RBAC は一切変更しません。エージェントが有効なセッションでは、RBAC で許可され、かつ RSS でも許可された操作だけが実行できます。どちらか一方でも許可していなければ、その操作はできません。

定義の構造

RSS の定義は、操作と対象範囲を定める privilege_scopes と、使えるロールを定める role_scopes の 2 セクションで構成されます。

privilege_scopes:
  allowed_privileges:
    - privileges: [<グループ権限>, ...]
      account: [all]                 # または databases: [...] / schemas: [...]
    - privileges: [data write]
      databases: [SANDBOX_DB, USER$] # USER$ はセッションユーザーの個人 DB に解決される
role_scopes:
  blocked_roles: [ACCOUNTADMIN, SYSADMIN]   # または allowed_roles
  blocked_secondary_roles: [ACCOUNTADMIN]    # または allowed_secondary_roles
  allow_role_switching: false                # デフォルトは true

privileges には個別の権限名ではなく、次のグループ権限を書きます。

グループ権限 対象 公式ドキュメントの定義
data read 全コンテナ テーブル・ビュー・ストリーム・ステージ・ワークスペースからの SELECT
data write 全コンテナ テーブルへのデータ書き込み(DDL は含まない)
object discovery 全コンテナ SHOW などによるオブジェクトの発見(データは読まない)
compute usage アカウント ウェアハウス・コンピュートプールの利用
program usage 全コンテナ UDF・ストアドプロシージャ・Streamlit の実行
grant management 全コンテナ GRANT / REVOKE
object management 全コンテナ 非機密オブジェクトのフルコントロール
full management アカウント アカウント内のすべての操作

ここで押さえておきたいのは、data write が「テーブルへのデータ書き込み」であり、CREATE TABLE のような DDL を含まない点です。

指定方法について

RSS を使用するには、セッションポリシーの AGENT_RESTRICTED_SESSION_SCOPE に RSS を指定し、そのポリシーをアカウントまたはユーザーに付けます。指定方法は 3 つあります。

-- (a) Snowflake 定義済みスコープを名前で参照
CREATE SESSION POLICY sp_a
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ';

-- (b) 自作の RSS オブジェクトを完全修飾名で参照
CREATE RESTRICTED SESSION SCOPE gov.policies.my_scope AS $$ ... $$;
CREATE SESSION POLICY sp_b
  AGENT_RESTRICTED_SESSION_SCOPE = 'GOV.POLICIES.MY_SCOPE';

-- (c) YAML をインラインで埋め込む
CREATE SESSION POLICY sp_c
  AGENT_RESTRICTED_SESSION_SCOPE = $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read]
      account: [all]
$$;

-- 適用先はアカウント全体またはユーザー単位
ALTER ACCOUNT SET SESSION POLICY sp_a;
ALTER USER jsmith SET SESSION POLICY sp_b;

Snowflake 定義済みのスコープは 3 種類です。

スコープ名 許可される操作 許可されない操作
SNOWFLAKE$DATA_READ アカウント全体の data read 書き込み・DDL・UDF・ストアドプロシージャ
SNOWFLAKE$DATA_READ_WITH_AI data read + object discovery + AI 関連オブジェクト(Agent、MCP Server、Cortex Search service)の USAGE 書き込み・DDL・ストアドプロシージャ
SNOWFLAKE$DATA_READ_PROGRAM_USAGE data read + program usage(UDF・ストアドプロシージャ・Streamlit アプリの実行) 書き込み・DDL

SNOWFLAKE$DATA_READ_WITH_AI の UDF の扱いは、公式ドキュメントの本文(UDF の USAGE を含むと読める)と付録の YAML 定義(Agent、MCP Server、Cortex Search service のみ)で記載に差があります。今回の検証では UDF の呼び出しは拒否されました(後述)。

いつ RSS が適用されるか

RSS が適用されるのは、セッションでエージェントが有効(agent-activated)なときだけです。SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') が TRUE を返す状況として、公式ドキュメントは次を挙げています。

  • Snowflake ネイティブのエージェント: Cortex Agents、CoCo 各クライアント(CLI / Desktop / Snowsight)経由の lite agent、CoWork、REST API 経由の呼び出し
  • 外部エージェント: Snowflake 管理の MCP Server 経由の接続、SERVICE_AGENT タイプのユーザー、IS_AGENTIC = TRUE の OAuth セキュリティ統合

ワークシートや dbt など、エージェントを介さないセッションでは RSS は無視されます。

どの RSS が適用されているかは、セッション内で次の関数で確認できます。

SELECT SYS_CONTEXT('SNOWFLAKE$CURRENT', 'ACTIVE_RESTRICTED_SESSION_SCOPES');

制限事項

公式ドキュメントの Considerations に記載されている仕様です。

制限事項
  • RSS は既存 RBAC の上限であり、権限を追加しません
  • 一度エージェント活動に対して RSS が有効化されると、そのセッション中に RSS を変更・解除・昇格することはできません。別のスコープを適用するには、新しいセッション(CoCo なら新しい会話)を開始します
  • ユーザーレベルのセッションポリシーがある場合、その AGENT_RESTRICTED_SESSION_SCOPE だけが使われます。アカウントレベルのポリシーとはマージされません
  • セッションポリシーに AGENT_RESTRICTED_SESSION_SCOPE がなければ、スコープは適用されません。非エージェントの操作では、この設定は無視されます
  • ウェアハウスの利用は privilege scope にかかわらず暗黙的に許可されます
  • blocked_roles はロール名の完全一致です。ロール階層を辿って継承元をブロックすることはなく、現在のプライマリロールが継承済みの権限も剥がしません
  • セッションポリシーから完全修飾名で参照されている RSS は、参照を外すまで DROP できません
  • CoCo CLI / CoCo Desktop でユーザー自身が RSS を管理する機能(user-managed RSS)は、2026年9月4日時点で Private Preview です。本記事が扱うセッションポリシー経由の RSS は GA です

前提条件

  • Snowflake: Enterprise エディション、AWS 東京リージョン
  • 必要な権限
    • RSS の作成: 格納先スキーマへの CREATE RESTRICTED SESSION SCOPE 権限。変更は RSS への MODIFY または OWNERSHIP、削除は OWNERSHIP
    • セッションポリシーの作成: 格納先スキーマへの CREATE SESSION POLICY 権限
    • セッションポリシーの適用: アカウントへの APPLY SESSION POLICY 権限

以下の SQL は CREATE OR REPLACE を使っているため、同名のデータベース・スキーマ・テーブルがあると内容を置き換えます。検証専用のアカウントか、専用のオブジェクト名で実行してください。

検証はすべて ACCOUNTADMIN で実施し、セッションポリシーは自分のユーザーにだけ適用しています。共有アカウントで ALTER ACCOUNT SET SESSION POLICY を実行すると、CoCo を使う全ユーザーに影響するためご注意ください。

検証の進め方

すべてのユースケースで、同じ SQL を CoCo(エージェントセッション)とワークシート(非エージェントセッション)の両方で実行し、RSS がエージェントにだけ作用することを比較します。結果は「RBAC」「RSS」「CoCo」「ワークシート」の 4 列で示し、「RBAC と RSS の両方で許可された操作だけが通る」ことを確認します。CoCo とワークシートは同じユーザー・同じデフォルトロール・同じウェアハウスを使い、SQL 本文だけを一致させて比較しています。

検証環境

次の 3 つのデータベースと、CoCo が起動時に使う検証ロールを用意します。

  • KAWABATA_RSS_PROD_DB: 本番相当 DB。検証専用スキーマ KAWABATA_RSS_SALES だけを使う
  • KAWABATA_RSS_SANDBOX_DB: サンドボックス DB。エージェントの書き込み先
  • KAWABATA_RSS_GOV_DB: ガバナンス DB。RSS とポリシーの格納先
USE ROLE ACCOUNTADMIN;

CREATE OR REPLACE DATABASE KAWABATA_RSS_PROD_DB;     -- 本番相当
CREATE OR REPLACE SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES;
CREATE OR REPLACE DATABASE KAWABATA_RSS_SANDBOX_DB;  -- エージェントの書き込み先
CREATE OR REPLACE SCHEMA KAWABATA_RSS_SANDBOX_DB.WORK;
CREATE OR REPLACE DATABASE KAWABATA_RSS_GOV_DB;      -- RSS とポリシーの格納先
CREATE OR REPLACE SCHEMA KAWABATA_RSS_GOV_DB.POLICIES;

本番相当の注文テーブルには、PII 列(customer_name, email)を含む架空データを 20 行入れます。note 列は検証マーカーで、エージェントが INSERT した行は 'RSS_TEST' で識別します。

サンプルデータの INSERT 文(クリックで展開)
CREATE OR REPLACE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS (
  order_id      INT,
  customer_name STRING,
  email         STRING,
  order_date    DATE,
  product       STRING,
  amount        NUMBER(10,2),
  region        STRING,
  note          STRING
);

-- 架空の EC 注文データ 20 行 (メールアドレスはすべて example.com)
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS VALUES
  ( 1, '佐藤 太郎',   'sato.taro@example.com',       '2026-08-01', 'Wireless Mouse',   2500.00, 'Kanto',    'SEED'),
  ( 2, '鈴木 花子',   'suzuki.hanako@example.com',   '2026-08-01', 'USB-C Hub',        4800.00, 'Kansai',   'SEED'),
  ( 3, '高橋 健',     'takahashi.ken@example.com',   '2026-08-02', 'Monitor 27inch',  32000.00, 'Kanto',    'SEED'),
  ( 4, '田中 美咲',   'tanaka.misaki@example.com',   '2026-08-02', 'Keyboard',         9800.00, 'Chubu',    'SEED'),
  ( 5, '伊藤 大輔',   'ito.daisuke@example.com',     '2026-08-03', 'Webcam',           6500.00, 'Kyushu',   'SEED'),
  ( 6, '渡辺 さくら', 'watanabe.sakura@example.com', '2026-08-03', 'Headset',          8900.00, 'Kanto',    'SEED'),
  ( 7, '山本 翔',     'yamamoto.sho@example.com',    '2026-08-04', 'Laptop Stand',     3900.00, 'Kansai',   'SEED'),
  ( 8, '中村 綾',     'nakamura.aya@example.com',    '2026-08-04', 'Wireless Mouse',   2500.00, 'Tohoku',   'SEED'),
  ( 9, '小林 拓也',   'kobayashi.takuya@example.com','2026-08-05', 'Docking Station', 18000.00, 'Kanto',    'SEED'),
  (10, '加藤 恵',     'kato.megumi@example.com',     '2026-08-05', 'USB-C Hub',        4800.00, 'Chubu',    'SEED'),
  (11, '吉田 直樹',   'yoshida.naoki@example.com',   '2026-08-06', 'Monitor 27inch',  32000.00, 'Kansai',   'SEED'),
  (12, '山田 優',     'yamada.yu@example.com',       '2026-08-06', 'Keyboard',         9800.00, 'Hokkaido', 'SEED'),
  (13, '佐々木 遥',   'sasaki.haruka@example.com',   '2026-08-07', 'Webcam',           6500.00, 'Kanto',    'SEED'),
  (14, '山口 亮',     'yamaguchi.ryo@example.com',   '2026-08-07', 'Headset',          8900.00, 'Kyushu',   'SEED'),
  (15, '松本 里奈',   'matsumoto.rina@example.com',  '2026-08-08', 'Laptop Stand',     3900.00, 'Kansai',   'SEED'),
  (16, '井上 誠',     'inoue.makoto@example.com',    '2026-08-08', 'Wireless Mouse',   2500.00, 'Kanto',    'SEED'),
  (17, '木村 彩花',   'kimura.ayaka@example.com',    '2026-08-09', 'Docking Station', 18000.00, 'Chubu',    'SEED'),
  (18, '林 悠斗',     'hayashi.yuto@example.com',    '2026-08-09', 'USB-C Hub',        4800.00, 'Tohoku',   'SEED'),
  (19, '斎藤 結衣',   'saito.yui@example.com',       '2026-08-10', 'Monitor 27inch',  32000.00, 'Kanto',    'SEED'),
  (20, '清水 陽介',   'shimizu.yosuke@example.com',  '2026-08-10', 'Keyboard',         9800.00, 'Kansai',   'SEED');

サンドボックス側の書き込み先テーブルと、program usage の判定に使うユーザー定義関数(UDF)・ストアドプロシージャも作ります。

CREATE OR REPLACE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (
  note_id    INT AUTOINCREMENT,
  note       STRING,
  created_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);

CREATE OR REPLACE FUNCTION KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO(v STRING)
  RETURNS STRING
  AS $$ 'echo: ' || v $$;

CREATE OR REPLACE PROCEDURE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING()
  RETURNS STRING
  LANGUAGE SQL
  EXECUTE AS CALLER
AS
$$
BEGIN
  RETURN 'pong from ' || CURRENT_ROLE();
END;
$$;

検証ロールと RBAC 側の権限

検証ロール KAWABATA_RSS_ANALYST には、本番相当・サンドボックスの対象テーブルに SELECT・INSERT(本番相当は DELETE も)、対象スキーマに CREATE TABLE を付けます。RBAC 上はどちらにも書ける状態にしておき、RSS でどこまで絞れるかを見るためです。

CREATE OR REPLACE ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON WAREHOUSE WH_SI_JP TO ROLE KAWABATA_RSS_ANALYST;

-- 本番相当: SELECT / INSERT / DELETE + CREATE TABLE
GRANT USAGE ON DATABASE KAWABATA_RSS_PROD_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE, CREATE TABLE ON SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES TO ROLE KAWABATA_RSS_ANALYST;
GRANT SELECT, INSERT, DELETE ON TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS TO ROLE KAWABATA_RSS_ANALYST;

-- サンドボックス: SELECT / INSERT + CREATE TABLE
GRANT USAGE ON DATABASE KAWABATA_RSS_SANDBOX_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE, CREATE TABLE ON SCHEMA KAWABATA_RSS_SANDBOX_DB.WORK TO ROLE KAWABATA_RSS_ANALYST;
GRANT SELECT, INSERT ON TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES TO ROLE KAWABATA_RSS_ANALYST;

-- ガバナンス: UDF / ストアドプロシージャの実行のみ
GRANT USAGE ON DATABASE KAWABATA_RSS_GOV_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON SCHEMA KAWABATA_RSS_GOV_DB.POLICIES TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON FUNCTION KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO(STRING) TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON PROCEDURE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING() TO ROLE KAWABATA_RSS_ANALYST;

GRANT ROLE KAWABATA_RSS_ANALYST TO USER KAWABATA_TOMOHIRO;

CoCo in Snowsight はユーザーのデフォルトロールでセッションを開始するため、検証中だけデフォルトロールを検証ロールに差し替えます。セカンダリロールが ALL だと、ユーザーが持つ全ロールの権限が RBAC 側に混ざるので空にしておきます。

ALTER USER KAWABATA_TOMOHIRO SET DEFAULT_ROLE = KAWABATA_RSS_ANALYST;
ALTER USER KAWABATA_TOMOHIRO SET DEFAULT_SECONDARY_ROLES = ();

試してみた

RSS がなければエージェントは RBAC の全権使用

まず、セッションポリシーを付けていない状態で CoCo に各操作を実行させます。
CoCo には次のように指示し、SQL を書き換えずに 1 文ずつ実行してもらいました。テスト行の order_date には実行日の CURRENT_DATE() が入るため、実行日によって値が変わります。

以下の SQL を、書き換えずに 1 文ずつそのまま実行して、各文の成功/失敗と結果、
エラーが出た場合はエラーメッセージ全文を表にまとめてください。
エラーが出ても中断せず次の文に進んでください。
SELECT
  CURRENT_ROLE()            AS CURRENT_ROLE,
  CURRENT_SECONDARY_ROLES() AS CURRENT_SECONDARY_ROLES,
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS IS_AGENT_ACTIVATED,
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'AGENT_TYPE')         AS AGENT_TYPE;

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (901, 'テスト 太郎', 'test901@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');
CREATE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.AGENT_BASELINE (id INT);
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P0 baseline');
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P0');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();

2026-09-04_20h00_52

7 文すべて成功しました。CURRENT_ROLEKAWABATA_RSS_ANALYSTIS_AGENT_ACTIVATEDTRUE で、エージェントセッションでも RBAC どおりに本番への INSERT と CREATE TABLE が通っています。これが RSS なしの状態です。

本番は読むだけ、書けるのはサンドボックスだけ

最初のユースケースは、公式ドキュメントの例にもある「本番は読み取り専用 + サンドボックスは書き込み可」です。アカウント全体に data readprogram usage を許し、data write はサンドボックス DB と個人 DB に限定します。

USE ROLE ACCOUNTADMIN;

CREATE OR REPLACE RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY
  COMMENT = 'UC-A: 本番は読むだけ。書けるのは sandbox と個人 DB'
  AS $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read, object discovery, program usage, compute usage]
      account: [all]
    - privileges: [data write]
      databases: [KAWABATA_RSS_SANDBOX_DB, USER$]
$$;

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY
  AGENT_RESTRICTED_SESSION_SCOPE = 'KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY';

-- 自分のユーザーにだけ適用する
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

なお、ウェアハウスの利用は privilege scope にかかわらず暗黙的に許可されるため、compute usage は説明のために明示しているだけで、なくても動作します。

作成した RSS は DESC で YAML をそのまま確認できます。セッションポリシー側には、従来のタイムアウトやセカンダリロールの項目に加えて AGENT_RESTRICTED_SESSION_SCOPE が表示されました。

DESC RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY;
DESC SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

2026-09-04_20h03_18

CoCo で新しい会話を開き、次の SQL を実行させます。

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (902, 'テスト 次郎', 'test902@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');
CREATE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.AGENT_T1 (id INT);
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P1 UC-A');
CREATE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_T1 (id INT);
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P1');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();

2026-09-04_20h06_33

操作 RBAC RSS CoCo ワークシート
SELECT 本番 許可 data read(account) 成功 成功
INSERT 本番 許可 本番に data write なし 拒否 成功
CREATE TABLE 本番 許可 なし 拒否 成功
INSERT サンドボックス 許可 data write(sandbox) 成功 成功
CREATE TABLE サンドボックス 許可 data write は DDL を含まない 拒否 成功
UDF / ストアドプロシージャ 許可 program usage 成功 成功

拒否時のエラーには、どの RSS が原因かが明示されます。

SQL access control error: Insufficient privileges to operate on table 'ORDERS'.
Restricted session scope KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY does not include
INSERT access to TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS.

RBAC 上は本番に書ける検証ロールのまま、エージェント経由の書き込みだけが止まっており、狙いどおりです。

もちろんワークシート上で実行すれば成功します。
2026-09-04_20h07_55

data write と object management は別物

data write は「テーブルへのデータ書き込み」で、CREATE TABLE のような DDL を含みません。サンドボックスにテーブルを作らせたい場合は object management を追加します。既存の RSS には ADD で YAML の断片をマージできます。公式ドキュメントの構文は ADD AS $$ ... $$ ですが、検証アカウントでは unexpected 'AS' の構文エラーになったため、AS を外して実行しています。

ALTER RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY ADD $$
privilege_scopes:
  allowed_privileges:
    - privileges: [object management]
      databases: [KAWABATA_RSS_SANDBOX_DB]
$$;

2026-09-04_20h14_40

新しい会話で CREATE TABLE を実行させた結果です。

2026-09-04_20h22_36

サンドボックスの CREATE TABLE だけが成功に変わり、本番への INSERT と CREATE TABLE は拒否のままです。RSS の変更は開いたままの会話には反映されないため、必ず新しい会話で確認してください。

特権ロールへの切り替えを止める

2つ目は、冒頭で挙げた「ロール切り替え」の課題です。role_scopes で ACCOUNTADMIN などをブロックし、allow_role_switching: false でロール切り替え自体を禁止します。セカンダリロールの有効化も blocked_secondary_roles で止めます。

ALTER RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY ADD $$
role_scopes:
  blocked_roles: [ACCOUNTADMIN, SYSADMIN, SECURITYADMIN]
  blocked_secondary_roles: [ACCOUNTADMIN, SYSADMIN, SECURITYADMIN]
  allow_role_switching: false
$$;

2026-09-04_20h29_38

新しい会話で、プライマリロールの切り替えとセカンダリロールの有効化を順に試させます。CoCo が出す Allow / Deny のダイアログはすべて Allow にし、RSS 側で止まるかを見ます。

USE ROLE ACCOUNTADMIN;
SELECT CURRENT_ROLE();
USE ROLE SYSADMIN;
SELECT CURRENT_ROLE();
USE ROLE KAWABATA_DBT_DEV_ROLE;   -- ブロック対象外だが切替禁止
SELECT CURRENT_ROLE();
USE SECONDARY ROLES ALL;
SELECT CURRENT_SECONDARY_ROLES();
USE SECONDARY ROLES ACCOUNTADMIN;
SELECT CURRENT_SECONDARY_ROLES();

2026-09-04_20h31_36

2026-09-04_20h33_45

CoCo の Allow を押しても、USE ROLEUSE SECONDARY ROLES はすべて次のエラーで拒否されました。プライマリロールは KAWABATA_RSS_ANALYST、セカンダリロールは空のまま変わっていません。ブロック対象外の KAWABATA_DBT_DEV_ROLE への切り替えも allow_role_switching: false で止まっています。

Current session is restricted. USE ROLE not allowed.

blocked_roles はロール名の完全一致で、ロール階層は辿りません。
ACCOUNTADMIN を継承する独自ロールがある場合は、そのロール名も明示的に書く必要があります。

定義済みスコープ 3 種を比べる

自作の YAML を書かなくても、Snowflake 定義済みの 3 スコープをセッションポリシーから名前で参照できます。それぞれを順にユーザーへ適用し、同じ SQL を CoCo に実行させて差を確認します。

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P4 DATA_READ');
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P4');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();
CREATE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_T3 (id INT);
SHOW TABLES IN SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES;

ユーザーに付けられるセッションポリシーは 1 つなので、差し替えは UNSET してから SET します。

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ';
CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ_WITH_AI
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ_WITH_AI';
CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ_PROGRAM_USAGE
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ_PROGRAM_USAGE';

ALTER USER KAWABATA_TOMOHIRO UNSET SESSION POLICY;
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ;
-- 以下、WITH_AI / PROGRAM_USAGE も同様に UNSET → SET

SNOWFLAKE$DATA_READ:アカウント全体の data read のみ
2026-09-04_20h49_08

SNOWFLAKE$DATA_READ_WITH_AI:data read + object discovery + AI 関連オブジェクト(Agent、MCP Server、Cortex Search service)の USAGE

2026-09-04_20h55_51

SNOWFLAKE$DATA_READ_PROGRAM_USAGE:data read + program usage(UDF・ストアドプロシージャ・Streamlit アプリの実行)

2026-09-04_20h58_13

3 種の結果をまとめると次のとおりです。

操作 DATA_READ DATA_READ_WITH_AI DATA_READ_PROGRAM_USAGE
SELECT 成功 成功 成功
SHOW TABLES 成功 成功 成功
INSERT サンドボックス 拒否 拒否 拒否
UDF 呼び出し 拒否 拒否 成功
CALL ストアドプロシージャ 拒否 拒否 成功

SNOWFLAKE$DATA_READ_WITH_AI でも UDF の呼び出しは拒否されました。公式ドキュメント本文には UDF を含むように読める記載がありますが、付録の YAML 定義どおり AI 関連オブジェクトの USAGE のみが許可されていると考えられます。利用時は対象アカウントでの実測を推奨します。
なお、CREATE TABLE は 3 回とも前の実行で作成済みのテーブルとの名前重複(Object already exists)で失敗したため、この表からは除いています。

インライン YAML

RSS オブジェクトを作らず、セッションポリシーに YAML を直接書く方法も試しました。DESC SESSION POLICYAGENT_RESTRICTED_SESSION_SCOPE 行には、RSS 名の代わりに YAML 本文がそのまま表示されます。

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_INLINE
  AGENT_RESTRICTED_SESSION_SCOPE = $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read]
      databases: [KAWABATA_RSS_PROD_DB]
$$;
DESC SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_INLINE;

2026-09-04_21h54_08

マスキングポリシーと RSS

最後に、既存の「エージェント時だけマスクする」マスキングポリシーと RSS を同じセッションで重ねます。
RSS は「触れる範囲」、マスキングは「触れた後に見える値」を制御するため、両者は競合せず補完し合うはずです。

-- IS_AGENT_ACTIVATED は Behavior Change Bundle 2026_06 の有無で VARCHAR / BOOLEAN が変わるため ::BOOLEAN で吸収
CREATE OR REPLACE MASKING POLICY KAWABATA_RSS_GOV_DB.POLICIES.MASK_EMAIL_FOR_AGENT
  AS (val STRING) RETURNS STRING ->
  CASE
    WHEN SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEAN = TRUE
      THEN REGEXP_REPLACE(val, '^[^@]+', '***')
    ELSE val
  END;

ALTER TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  MODIFY COLUMN email SET MASKING POLICY KAWABATA_RSS_GOV_DB.POLICIES.MASK_EMAIL_FOR_AGENT;

-- セッションポリシーは UC-A の RSS_PROD_READONLY に戻しておく
ALTER USER KAWABATA_TOMOHIRO UNSET SESSION POLICY;
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

CoCo とワークシートの両方で、IS_AGENT_ACTIVATEDCURRENT_ROLE()email を同時に出力し、続けて本番への INSERT を実行します。

SELECT
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS IS_AGENT_ACTIVATED,
  CURRENT_ROLE() AS CURRENT_ROLE,
  order_id, customer_name, email
FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
ORDER BY order_id
LIMIT 3;

INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (903, 'テスト 三郎', 'test903@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');

マスキングポリシー
2026-09-04_22h05_48

2026-09-04_22h07_00

INSERT
2026-09-04_22h09_57

2026-09-04_22h10_48

同じユーザー・同じロールで、エージェントのときだけ「本番に書けず、メールアドレスも見えない」状態になっており、2 層の防御が同時に作動しています。

適用状況の確認

適用状況は POLICY_REFERENCES で確認できます。公式ドキュメントの例に合わせてポリシー名から引く方法と、ユーザー側から逆引きする方法の両方を試しました。

-- ポリシー側から適用先を確認
SELECT POLICY_NAME, POLICY_KIND, REF_ENTITY_NAME, REF_ENTITY_DOMAIN, POLICY_STATUS
FROM TABLE(KAWABATA_RSS_GOV_DB.INFORMATION_SCHEMA.POLICY_REFERENCES(
  POLICY_NAME => 'KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY'));

2026-09-04_22h19_53


-- ユーザー側から逆引き
SELECT POLICY_NAME, POLICY_KIND, REF_ENTITY_NAME, REF_ENTITY_DOMAIN, POLICY_STATUS
FROM TABLE(KAWABATA_RSS_GOV_DB.INFORMATION_SCHEMA.POLICY_REFERENCES(
  REF_ENTITY_NAME => 'KAWABATA_TOMOHIRO', REF_ENTITY_DOMAIN => 'USER'));

2026-09-04_22h20_52

ポリシー名から引くと適用先のユーザーが 1 行、ユーザー側から逆引きすると、そのユーザーに付いているネットワークポリシーとセッションポリシーの 2 行が返りました。

制限事項・注意点

制限事項・注意点(クリックで展開)

公式ドキュメント記載の仕様

  • RSS は既存 RBAC の上限で、権限を追加しません。RSS で許可していても RBAC に権限がなければ実行できません
  • 一度エージェント活動に対して有効化された RSS は、そのセッション中に変更・解除・昇格できません。RSS を変更したら新しい会話で確認してください
  • 公式ドキュメントの ALTER RESTRICTED SESSION SCOPE ... ADD AS $$...$$ 構文は、検証アカウントでは構文エラーになりました。AS を外した ADD $$...$$ で成功します(REMOVE AS は未検証)
  • ユーザーレベルのセッションポリシーはアカウントレベルを完全に上書きし、マージされません
  • blocked_roles はロール名の完全一致で、ロール階層を辿りません。継承済みの権限も剥がしません
  • 参照中の RSS は DROP できません。先にセッションポリシー側の参照を外します
  • CoCo CLI / CoCo Desktop の user-managed RSS は 2026年9月4日時点で Private Preview です

今回の検証で確認した挙動(2026年9月4日時点)

  • SYS_CONTEXT('SNOWFLAKE$CURRENT', 'ACTIVE_RESTRICTED_SESSION_SCOPES') は、非エージェントのワークシートでは参照自体が invalid property エラーになりました。公式ドキュメントでは RSS が有効でなければ NULL を返す説明のため、検証時点のリリースやクライアントによる差と考えられます。エージェントセッションでの戻り値は本記事では確認していません
  • IS_AGENT_ACTIVATED は Behavior Change Bundle 2026_06 が無効な環境では VARCHAR を返します。ポリシー内では ::BOOLEAN で吸収してください
  • 個人データベース(USER$)へのテーブル作成は、検証アカウントでは 060119: Tables cannot currently be created in a personal database. で拒否されました。USER$ への書き込み許可は本記事では実測していません

検証で扱っていないこと

  • アカウントレベルとユーザーレベルのセッションポリシーの優先順位(共有アカウントのため ALTER ACCOUNT を実行していません)
  • CoCo CLI / CoCo Desktop、MCP Server、SERVICE_AGENT ユーザーなど、CoCo in Snowsight 以外の経路
  • RSS 適用によるクエリ性能・コストへの影響

おわりに

「エージェントに何をさせないか」は、これまでロールを分けるか、事後に検知するかの二択でした。RSS はその間に「人と同じロールのまま、エージェントのときだけ使える操作を絞る」という 3 つ目の選択肢を置いてくれます。
AI利活用するためのガバナンスを担う良い機能だと感じました。

この記事が何かの参考になれば幸いです!


Snowflake World Tour Tokyo 2026に参加しませんか?

Snowflakeの国内最大級イベント「Snowflake World Tour Tokyo」が2026年9月10日(木)・11日(金)にグランドプリンスホテル新高輪にて開催されます。
最新のAI・データ活用事例やライブデモを体感できる無料イベントです。

Snowflake World Tour Tokyoイベントに参加する


Snowflake Community Awards ファイナリストに選出されました

DevelopersIO で Snowflake 記事を執筆している かわばた が、Snowflake Community Awards「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
最終選考の30%はコミュニティ投票です。記事がお役に立っていたようでしたら、9月15日(火)までにぜひ一票お願いします。フォームの「(4 of 6) RISING COMMUNITY LEADER OF THE YEAR」で Tomohiro Kawabata | Classmethod, Japan を選択、2分ほどで完了します。

投票フォームを開く


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

この記事をシェアする

関連記事