acl-security について
ServiceNow の開発者や管理者向けに、ACL の適用規則、ロール確認、一般的な設定パターンを解説するガイドです。継承テーブルやワイルドカードの名前、診断の限界、安全な ACL 作成手順を扱います。
主な機能
- 01レコード/フィールド ACL の整理
- 02継承テーブルとワイルドカード規則の比較
- 03ルールごとのロール差分の説明
- 04ACL の作成と確認手順
- 05一般的なアクセス制御パターン
活用シーン
- 01レコードやフィールドのアクセス障害の調査
- 02ロールや所有権に基づくアクセス設計
- 03ACL 作成後のロール設定確認
MCP 対応
適用される ACL の収集に使う snow_acl_explain、ACL の枠を作成する snow_create_acl、監査付き偽装リンクを作る snow_impersonate_user など、ServiceNow の MCP ツールを説明します。判定を誤って報告する可能性がある snow_test_acl には依存しないよう注意しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ serac-labs/ serac --skill acl-securityインストール後、Claude Code で/acl-securityを入力して読み込まれたか確認します。
詳細
ServiceNow の ACL セキュリティパターン
アクセス制御リスト(ACL)は、ServiceNow のセキュリティの基盤です。レコードの読み取り、書き込み、作成、削除を行えるユーザーを制御します。
適用される ACL
ACL は名前で適用され、その名前はリテラルです。つまり、sys_security_acl.name に格納されるのは incident.priority や * であり、グロブパターンではありません。別のテーブルを拡張するテーブルのあるフィールドには、次の 6 つの名前のそれぞれにルールを設定できます。
| 名前 | ルールが適用される対象 |
|---|---|
incident.priority | このテーブルのこのフィールド |
task.priority | 親テーブルのこのフィールド |
*.priority | あらゆるテーブルにある、この名前のフィールド |
incident.* | このテーブルのあらゆるフィールド |
task.* | 親テーブルのあらゆるフィールド |
*.* | あらゆるテーブルのあらゆるフィールド |
これらに加えて、レコードレベルの名前もあります。incident、次に sys_db_object.super_class の階層にある各親テーブル、そして * です。
これらの形式のうち、ライブインスタンスで確認されているものが 2 つあります。レコードレベルの *(sn-roles.manifest.json の背後にある ACL プローブは、これを通じて 189 個のツールプリミティブを解決しました)と、table.field(snow_analyze_form は nameSTARTSWITH<table>. を使ってフィールド ACL を読み取ります)です。上記の *.priority、incident.*、*.* に関する説明は同じ命名方式に基づいていますが、ServiceNow 自身のドキュメントを出典としていません。文書化された動作ではなく、確認する価値のある名前として扱ってください。snow_acl_explain はいずれにしてもすべてを確認し、使われていない名前は何も返しません。
これらすべてを確認してください。incident.priority だけを調べて止めてしまうと、デバッグ対象のまさにそのケースで、誤ったブロッカーを特定することになります。ルールが task またはワイルドカードに設定されている場合です。
snow_acl_explain は、1 回の呼び出しですべてを収集します。親テーブル名については sys_db_object.super_class をたどり、一致する各ルールの必須ロールを sys_security_acl_role の m2m から読み取り(sys_security_acl に roles 列はないため、そこを読み取るものはすべてロールなしと報告します)、それらをユーザーが sys_user_has_role で保持しているロールと比較します。ここにはすでに継承されたロールも含まれているため、自分で sys_user_role_contains を展開する必要はありません。
この差分は正しく読み取ってください。1 つのルールに設定されたロールは選択肢なので、ルールの roles_not_held は requires_roles の残りの部分であり、付与すべきロールの一覧ではありません。そのルールの user_holds が空でなければ、ロールチェックはすでに通過しており、ブロッカーは別の場所、つまり別の階層か、ツールが評価しなかった条件またはスクリプトにあります。
このツールが行わないのは、判定を下すことです。条件と高度なスクリプトは、プラットフォーム内で特定の 1 レコードに対して実行されます。REST Table API からそれらを評価する方法はないため、出力は「適用されるものと、ユーザーが保持しているもの」として読み取り、その後、ユーザーになりすまして確認してください。snow_test_acl は判定そのものを返すと称していますが、レコードを sys_script_execution に POST し、Table API のエコーをスクリプト出力のように報告するうえ、operation と user の両引数を無視します。使用しないでください。
ACL の種類
| 種類 | 制御対象 | 例 |
|---|---|---|
| record | 行レベルのアクセス | ユーザーはこのインシデントを表示できますか? |
| field | フィールドレベルのアクセス | ユーザーは assignment_group を表示できますか? |
| client_callable_script_include | Script Include へのアクセス | ユーザーはこの API を呼び出せますか? |
| ui_page | UI Page へのアクセス | ユーザーはこのページを表示できますか? |
| rest_endpoint | REST API へのアクセス | ユーザーはこのエンドポイントを呼び出せますか? |
MCP 経由での ACL の作成
snow_create_acl は ACL のシェルのみを作成し、それ以外は何も行いません。name、operation、type、admin_overrides、active を受け取りますが、roles、condition、script の引数はありません。これらを渡しても黙って無視され、sys_security_acl に渡されることはありません。この段階のままの ACL にはロールも条件も設定されておらず、記述できるルールの中で最も制限が緩い状態です。他の 2 つの手順が完了するまでは、active: false に設定してください。
// 1. The shell — a record-level WRITE ACL on incident, created inactive.
snow_create_acl({
name: "incident",
operation: "write",
type: "record",
admin_overrides: true,
active: false,
})
// 2. The roles. They live in the sys_security_acl_role m2m, one row per role, so
// this is one call per role and each one needs that role's sys_id.
snow_query_table({ table: "sys_user_role", query: "nameINitil,incident_manager", fields: ["sys_id", "name"] })
snow_create_acl_role({ acl: "<acl sys_id>", role: "<itil sys_id>" })
snow_create_acl_role({ acl: "<acl sys_id>", role: "<incident_manager sys_id>" })
// 3. Condition and script are columns on the ACL record itself, with no argument
// on snow_create_acl. Set them by updating the record, then activate it.
snow_record_manage({
action: "update",
table: "sys_security_acl",
sys_id: "<acl sys_id>",
data: { script: "answer = current.state < 6;", active: true },
})
1 つの ACL に設定した 2 つのロールは代替関係にあります。どちらか一方で、そのルールに基づくアクセスが許可されます。そのため、手順 2 では行を追加するたびにルールの範囲が広がり、狭まることはありません。
condition と script はどちらも sys_security_acl の実際のカラムです(snow_analyze_form で値を読み戻せます)。ACL フォームの Condition フィールドと Script フィールドが求める形式で、それぞれ記述してください。
フィールドレベルのルールも、name: "incident.priority" を指定する以外は同じ 3 つの手順です。
作業内容は、snow_acl_explain({ table: "incident", operation: "write", user: "<someone>" }) で確認してください。これは m2m からロールを読み戻します。最も省略されやすい手順です。
よくある ACL パターン
パターン 1:ロールベースのアクセス
// Condition: (empty - role check only)
// Roles: itil, incident_manager
// Script: (empty)
// Users with itil OR incident_manager role can access
パターン 2:所有権ベースのアクセス
// Condition:
current.caller_id == gs.getUserID() || current.assigned_to == gs.getUserID() || current.opened_by == gs.getUserID()
// User can access their own records
パターン 3:グループベースのアクセス
// Script:
;(function () {
var userGroups = gs.getUser().getMyGroups()
answer = userGroups.indexOf(current.assignment_group.toString()) >= 0
})()
// User can access records assigned to their groups
パターン 4:マネージャーチェーンによるアクセス
// Script:
;(function () {
var callerManager = current.caller_id.manager
var currentUser = gs.getUserID()
// Check if current user is in caller's management chain
while (callerManager && !callerManager.nil()) {
if (callerManager.toString() == currentUser) {
answer = true
return
}
callerManager = callerManager.manager
}
answer = false
})()
パターン 5:時間ベースのアクセス
// Script:
;(function () {
var now = new GlideDateTime()
var hour = parseInt(now.getLocalTime().getHourOfDayLocalTime())
// Only allow access during business hours (8 AM - 6 PM)
answer = hour >= 8 && hour < 18
})()
パターン 6:データ分類
// Script:
;(function () {
var classification = current.u_data_classification.toString()
var userClearance = gs.getUser().getRecord().getValue("u_security_clearance")
var levels = { public: 0, internal: 1, confidential: 2, secret: 3 }
answer = levels[userClearance] >= levels[classification]
})()
フィールドレベルのセキュリティパターン
機密フィールドを非表示にする
// ACL: incident.u_ssn (Social Security Number)
// Operation: read
// Script:
answer = gs.hasRole("hr_admin")
// Only HR admins can see SSN field
状態変更後に読み取り専用にする
// ACL: incident.short_description
// Operation: write
// Script:
answer = current.state < 6 // Can't edit after Resolved
// Prevent editing after resolution
条件付きフィールド表示
// ACL: incident.u_internal_notes
// Operation: read
// Condition:
gs.hasRole("itil") || current.caller_id == gs.getUserID()
// ITIL users see all, callers see their own
セキュリティのベストプラクティス
1. 最小権限の原則
// ❌ BAD - Too permissive
// Roles: (empty) - allows everyone
// ✅ GOOD - Explicit roles
// Roles: itil, incident_manager
2. デフォルトで拒否する
// Create a catch-all deny ACL at lowest priority
// Name: *
// Operation: read
// Condition: false
// This ensures anything not explicitly allowed is denied
3. 複雑なスクリプトを避ける
// ❌ BAD - Complex script ACL (slow)
;(function () {
var gr = new GlideRecord("sys_user_grmember")
gr.addQuery("user", gs.getUserID())
gr.query()
while (gr.next()) {
// Complex logic...
}
})()
// ✅ GOOD - Use conditions when possible
// Condition: gs.getUser().isMemberOf(current.assignment_group)
4. ACL を十分にテストする
// Use "Impersonate User" to test ACLs as different users
// Check: Navigation, List views, Forms, Related lists
// Verify: Fields hidden, buttons disabled, records filtered
snow_impersonate_user を使用すると、監査対象のなりすまし用ディープリンクを生成できます(管理者のみ使用可能。~/.serac/audit/impersonations.jsonl に書き込みます)。ACL の失敗を診断する前に、snow_session_context を使用して、呼び出し元の現在のロールと更新セットを確認してください。
ACL のデバッグ
ACL デバッグを有効にする
// In a background script or temporarily in your code:
gs.setProperty("glide.security.debug", "true")
gs.log("ACL Debug enabled")
// Check System Logs for ACL evaluation details
ユーザー権限を確認する
// Check if current user can read a record
var gr = new GlideRecord("incident")
gr.get("sys_id_here")
gs.info("Can Read: " + gr.canRead())
gs.info("Can Write: " + gr.canWrite())
gs.info("Can Delete: " + gr.canDelete())
// Check field-level
gs.info("Can read assignment_group: " + gr.assignment_group.canRead())
gs.info("Can write assignment_group: " + gr.assignment_group.canWrite())
よくある間違い
| 間違い | 問題 | 解決策 |
|---|---|---|
| カスタムテーブルに ACL がない | 誰でもアクセスできる | すぐに ACL を作成する |
| ロールベースの ACL のみ | 行レベルのセキュリティがない | データ分離の条件を追加する |
| DB を照会するスクリプト | パフォーマンスの問題 | 条件を使用するか、結果をキャッシュする |
| 管理者としてのみテストする | 管理者は ACL を迂回する | 実際のエンドユーザーとしてテストする |
| REST API を忘れる | API は UI の ACL を迂回する | 専用の REST ACL を作成する |