![[新機能]Snowflake の Restricted Session Scope が一般提供になったので CoCo 利用時だけ実効権限を狭めてみた](https://images.ctfassets.net/ct0aopd36mqt/wp-refcat-img-3610e3c1ff5961bdb7b464e17f8bf06d/90b168b240005ead852ec1d474bb74fb/snowflake-logo-1200x630-1.png?w=3840&fm=webp)
[新機能]Snowflake の Restricted Session Scope が一般提供になったので CoCo 利用時だけ実効権限を狭めてみた
かわばたです。
2026年9月3日にRestricted Session Scope(RSS)が一般提供となりました。
実務で想定される 4 つのユースケースで CoCo in Snowsight から検証します。
【追記】
Snowflake Community Awards の「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
詳細は下記よりご確認ください。
先に結論
- 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 | エージェント時に、どの操作・どの範囲に触れられるか | セッション(エージェント有効時) |
本記事では、この位置づけを踏まえて次の流れで進めます。
- これまでの課題
- RSS とは
- 検証
- おわりに
これまでの課題
Snowflake のアクセス制御は RBAC が基本です。ロールに権限を付け、ユーザーにロールを付ける。この仕組みは「誰が」の単位で権限を決めるため、同じユーザーが人としてワークシートから操作しても、エージェント経由で操作しても、使える権限は変わりません。
人が使う前提で設計した分析ロールに INSERT や CREATE TABLE が付いていれば、CoCo も同じ操作ができます。さらに CoCo はユーザーのデフォルトロールでセッションを始めますが、会話の流れで「ロールを切り替えて」と指示すれば USE ROLE を実行できます。確認ダイアログは出るものの、事前に禁止する手段がありませんでした。
IS_AGENT_ACTIVATED を条件にしたマスキングポリシーで「見える値」を絞る方法もありますが、これは「触れた後」の話です。
下記で検証しているので、詳細が気になる方はご確認ください。
「触れる範囲」そのものを、エージェントのときだけ狭めることができるのが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権限
- RSS の作成: 格納先スキーマへの
以下の 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();

7 文すべて成功しました。CURRENT_ROLE は KAWABATA_RSS_ANALYST、IS_AGENT_ACTIVATED は TRUE で、エージェントセッションでも RBAC どおりに本番への INSERT と CREATE TABLE が通っています。これが RSS なしの状態です。
本番は読むだけ、書けるのはサンドボックスだけ
最初のユースケースは、公式ドキュメントの例にもある「本番は読み取り専用 + サンドボックスは書き込み可」です。アカウント全体に data read と program 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;

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

| 操作 | 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 上は本番に書ける検証ロールのまま、エージェント経由の書き込みだけが止まっており、狙いどおりです。
もちろんワークシート上で実行すれば成功します。

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

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

サンドボックスの 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
$$;

新しい会話で、プライマリロールの切り替えとセカンダリロールの有効化を順に試させます。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();


CoCo の Allow を押しても、USE ROLE と USE 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 のみ

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

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

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 POLICY の AGENT_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;

マスキングポリシーと 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_ACTIVATED・CURRENT_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');
マスキングポリシー


INSERT


同じユーザー・同じロールで、エージェントのときだけ「本番に書けず、メールアドレスも見えない」状態になっており、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'));

-- ユーザー側から逆引き
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'));

ポリシー名から引くと適用先のユーザーが 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利活用するためのガバナンスを担う良い機能だと感じました。
この記事が何かの参考になれば幸いです!





