有效访问分析
1. 简介
现代组织往往难以回答一个看似简单的问题:谁可以访问什么,以及为什么?
访问权限很少是直接授予的。它随着时间的推移,通过分布在云平台、SaaS 应用程序和内部系统中的角色、组和策略不断累积,形成了难以察觉或解释的“访问蔓延”现象。
虽然传统的身份与访问管理 (IAM) 工具提供了必要的控制手段,但它们通常关注的是访问权限如何分配,而非用户实际拥有的访问权限。如果没有对有效访问权限的清晰视图,组织将面临审计压力增加、过度授权以及敏感资源意外暴露等问题。
2. 场景
大多数组织通过组、角色和直接分配的组合来管理对文档、应用程序和系统的访问,这些通常分布在多个身份平台上。
员工通常被划分到财务、运营和工资核算等部门,通过共享角色和嵌套组成员身份来授予访问权限。随着时间的推移,基于项目的组、临时角色分配和业务例外情况会引入额外的访问路径,这意味着单个人员可能通过多条独立的路线访问同一个资源。
IT 系统的操作访问权限通常是单独管理的。承包商和托管服务提供商可能存在于核心员工目录之外,但仍需要对内部系统进行特权访问。
虽然访问控制可能在各个系统内部配置正确,但这种结构带来了几个持续的挑战:
-
碎片化的可见性:难以掌握谁可以访问特定资源
-
间接和继承的访问权限:难以追溯到其源头
-
重叠的访问路径:由于组嵌套、项目或例外情况而无意中创建
-
有限的可解释性:在访问审查、审计或补救期间
传统的访问审查和报告难以奏效,因为访问权限并非集中在一个地方分配。它是由身份、组、角色和跨系统的授权之间的相互作用而产生的。
有效访问分析通过关注访问权限如何实际授予和继承来解决这一问题,从而产生清晰、可解释的结果,反映真实的访问情况,而非仅仅是预期的策略。
3. 解决方案
有效访问分析关注的是访问权限在整个组织中是如何实际产生的,而不仅仅是在单个系统中是如何配置的。
对文档、应用程序和系统的访问是由分布在多个平台上的组、角色、继承和例外情况相互作用的结果,这使得从整体上进行推理变得困难。
Neo4j 将访问权限建模为一个连接的关系系统。通过将身份、组、角色、授权和资源表示为图,有效访问权限可以通过遍历授予访问权限的路径来动态计算,从而反映真实世界的访问,而非静态分配。
这使组织能够在不同领域(从业务文档到操作 IT 系统)中一致地回答关键的访问问题,而无需更改现有的 IAM 平台或执行机制。
3.1. 为什么选择图数据库?
访问结构本质上是关系型和多路径的。单个身份可能通过几条独立的路径访问同一个资源,涉及不同的组、角色或授权。传统的关系型方法在没有复杂的联接、递归逻辑或自定义报告层的情况下,难以表示和解释这种访问继承。
图数据库非常适合这一挑战,因为它们:
- 将访问建模为关系,而非表
-
身份、组、角色、授权和资源被显式连接,反映了实践中访问权限如何被授予和继承。
- 自然地处理嵌套和间接访问
-
可以直接遍历组嵌套和角色继承,无需递归查询或预计算视图。
- 透明地暴露多条访问路径
-
当存在多条访问路径时,可以发现并比较所有路径——包括那些非预期的或不太明显的路径。
- 设计上具备可解释性
-
查询不仅返回谁拥有访问权限,还通过揭示所涉及的组、角色和授权的完整链条,说明了“为什么”。
- 在访问域之间保持灵活性
-
即使由不同的团队或工具管理,对文档和数据的业务访问以及对 IT 系统的操作访问也可以共存于同一个图中。
4. 建模
有效访问分析侧重于访问权限是如何导出的,而不是在任何单个系统中是如何配置的。该模型的目标是以一种在不同访问域之间保持一致、可解释且可扩展的方式表示访问关系。
Neo4j 将访问表示为身份、组、角色、授权和资源的连接图。这些元素以反映真实组织中访问权限如何被实际授予和继承的方式链接,从而允许直接从图结构中分析有效访问权限。
4.1 数据模型
核心访问模型围绕五个主要概念构建:
- 身份 (Identity)
-
身份代表一个承载访问权限的主体,例如用户或服务账户。身份独立于组织或人力资源结构进行建模,确保访问分析关注的是访问权限如何被授予,而不是身份数据源自何处。
- 组
-
组 (Group) 代表身份的逻辑集合。组可以嵌套,以反映组织结构、功能团队或基于项目的访问权限,从而使间接和继承的访问权限变得显式。
- 角色 (Role)
-
角色代表一种可重用的访问构建块,用于组合相关的访问权限。角色可以分配给组,也可以直接分配给身份,以支持例外情况、临时访问或操作需求。
- 授权 (Entitlement)
-
授权代表一种访问权限,描述了可以在资源上执行的操作。授权抽象了低级系统权限,使模型与系统无关,并与访问权限的审查和推理方式保持一致,而不会引入执行或策略评估语义。
- 资源 (Resource)
-
资源代表任何被授予访问权限的事物,例如文档、文件夹、应用程序或 IT 系统。
访问权限通过遍历以下关系得出:
-
(Identity)-[:MEMBER_OF]→(Group) -
(Group)-[:MEMBER_OF]→(Group)(嵌套组) -
(Group)-[:GRANTS_ROLE]→(Role) -
(Identity)-[:ASSIGNED_ROLE]→(Role)(直接分配) -
(Role)-[:GRANTS]→(Entitlement) -
(Entitlement)-[:APPLIES_TO]→(Resource)
此结构支持:
-
通过组嵌套进行的间接访问
-
对同一资源的多个独立访问路径
-
作为真实世界例外的直接角色分配
-
通过显式遍历路径实现的可解释性
4.1.1 必填字段
为了实现有效访问分析,仅需最少量的属性集:
Identity(身份)节点
-
id: 身份的唯一标识符 -
type: 身份类型(例如:员工、承包商、服务账户)
Group(组)节点
-
id: 组的唯一标识符 -
name: 人类可读的组名
Role(角色)节点
-
id: 角色的唯一标识符 -
name: 人类可读的角色名
Entitlement(授权)节点
-
action: 授权允许的操作(例如:读取、写入、管理)
Resource(资源)节点
-
id: 资源的唯一标识符 -
type: 资源类型(例如:文档、文件夹、应用程序、系统)
诸如来源系统、策略源或所有权等可选属性可以根据需要分层添加到模型中,但对于导出或解释有效访问权限并非必需。
4.2. 演示数据
此数据集专注于文档和共享文件夹,并包含少量 IT 资产,以展示相同的访问建模模式适用于不同的访问域。
访问权限通过部门和基于项目的组、嵌套团队、共享角色以及偶尔的直接角色分配来管理,从而导致继承和重叠的访问路径。
对 IT 系统的操作访问被单独建模以反映外包支持场景,同时遵循相同的访问建模和遍历模式。
数据集特意保持精简以便于理解,但足够形象化以揭示非明显的访问路径并展示实际中的有效访问。
// ------------------
// Constraints
//-------------------
CREATE CONSTRAINT identity_id
FOR (i:Identity) REQUIRE i.id IS UNIQUE;
CREATE CONSTRAINT group_id
FOR (g:Group) REQUIRE g.id IS UNIQUE;
CREATE CONSTRAINT role_id
FOR (r:Role) REQUIRE r.id IS UNIQUE;
CREATE CONSTRAINT resource_id
FOR (r:Resource) REQUIRE r.id IS UNIQUE;
//--------------------
// Create Identities
//--------------------
CREATE (alice:Identity {id:"alice", type:"user"}) // Operations employee
CREATE (bob:Identity {id:"bob", type:"user"}) // Payroll specialist
CREATE (charlie:Identity {id:"charlie", type:"user"}) // Outsourced IT support contractor
CREATE (dana:Identity {id:"dana", type:"user"}) // Direct access exception
CREATE (svcBackup:Identity {id:"svc-backup", type:"service_account", status:"active"}) //Service account
//--------------------
// Create Groups
//--------------------
CREATE (gEmployees:Group {id:"group-emp", name:"Employees"})
CREATE (gOps:Group {id:"group-ops", name:"Operations"})
CREATE (gFinance:Group {id:"group-fin", name:"Finance"})
CREATE (gPayroll:Group {id:"group-payroll", name:"Payroll Team"})
CREATE (gIT:Group {id:"group-it", name:"IT Support (Outsourced)"})
CREATE (gProcProject:Group {id:"group-proc-project", name:"Procurement Project"})
//-------------------------
// Create Group Hierarchies
//-------------------------
CREATE (gPayroll)-[:MEMBER_OF]->(gFinance)
CREATE (gFinance)-[:MEMBER_OF]->(gEmployees)
CREATE (gOps)-[:MEMBER_OF]->(gEmployees)
//--------------------
// Create Roles
//--------------------
CREATE (rReader:Role {id:"role-content-reader", name:"Content Reader", system:"content"})
CREATE (rEditor:Role {id:"role-content-editor", name:"Content Editor", system:"content"})
CREATE (rPayroll:Role {id:"role-payroll-processor", name:"Payroll Processor", system:"hr"})
CREATE (rITOps:Role {id:"role-it-operator", name:"IT Operator", system:"it"})
//--------------------
// Create Entitlements
//--------------------
CREATE (eRead:Entitlement {action:"read"})
CREATE (eEdit:Entitlement {action:"edit"})
CREATE (eViewPayslips:Entitlement {action:"view_payslips"})
CREATE (eApprovePayroll:Entitlement {action:"approve_payroll"})
CREATE (eLogin:Entitlement {action:"login"})
CREATE (eRestart:Entitlement {action:"restart_service"})
//--------------------------------------------------
// Create Resources (documents/folders + IT assets)
//--------------------------------------------------
CREATE (resKB:Resource {id:"folder-knowledge-base", type:"folder"})
CREATE (resVendorContracts:Resource {id:"doc-vendor-contracts", type:"document"})
CREATE (resPayrollFolder:Resource {id:"folder-payroll", type:"folder"})
CREATE (resPayslips:Resource {id:"doc-payslips-q4", type:"document"})
CREATE (resHRPortal:Resource {id:"app-hr-portal", type:"application"})
CREATE (resFileServer:Resource {id:"server-fileshare-01", type:"server"})
//-------------------------------------
// Create Identity → Group memberships
//-------------------------------------
CREATE (alice)-[:MEMBER_OF]->(gOps)
CREATE (bob)-[:MEMBER_OF]->(gPayroll)
CREATE (bob)-[:MEMBER_OF]->(gProcProject)
CREATE (charlie)-[:MEMBER_OF]->(gIT)
CREATE (svcBackup)-[:MEMBER_OF]->(gIT)
CREATE (dana)-[:MEMBER_OF]->(gEmployees)
//-----------------------------
// Create Group → Role grants
//-----------------------------
CREATE (gEmployees)-[:GRANTS_ROLE]->(rReader)
CREATE (gFinance)-[:GRANTS_ROLE]->(rEditor)
CREATE (gPayroll)-[:GRANTS_ROLE]->(rPayroll)
CREATE (gProcProject)-[:GRANTS_ROLE]->(rEditor)
CREATE (gIT)-[:GRANTS_ROLE]->(rITOps)
//-----------------------------------
// Direct role assignment (exception)
//-----------------------------------
CREATE (dana)-[:ASSIGNED_ROLE]->(rEditor)
//----------------------------------
// Create Role → Entitlement grants
//----------------------------------
CREATE (rReader)-[:GRANTS]->(eRead)
CREATE (rEditor)-[:GRANTS]->(eRead)
CREATE (rEditor)-[:GRANTS]->(eEdit)
CREATE (rPayroll)-[:GRANTS]->(eViewPayslips)
CREATE (rPayroll)-[:GRANTS]->(eApprovePayroll)
CREATE (rITOps)-[:GRANTS]->(eLogin)
CREATE (rITOps)-[:GRANTS]->(eRestart)
//-------------------------------
// Create Entitlement → Resource
//-------------------------------
CREATE (eRead)-[:APPLIES_TO]->(resKB)
CREATE (eRead)-[:APPLIES_TO]->(resVendorContracts)
CREATE (eEdit)-[:APPLIES_TO]->(resVendorContracts)
CREATE (eApprovePayroll)-[:APPLIES_TO]->(resPayrollFolder)
CREATE (eViewPayslips)-[:APPLIES_TO]->(resPayslips)
CREATE (eLogin)-[:APPLIES_TO]->(resHRPortal)
CREATE (eRestart)-[:APPLIES_TO]->(resFileServer)
数据集概览如下:
5. Cypher 查询
5.1. 该身份可以访问哪些资源?
遍历访问图以计算某个身份的有效访问权限和授权。
MATCH p=(i:Identity {id:"bob"})-->{1,50}(e:Entitlement)-[:APPLIES_TO]->(r:Resource)
RETURN r.id AS resource, e.action AS action
ORDER BY resource, action
下表显示了此查询的结果。
| resource(资源) | action(动作) |
|---|---|
"doc-payslips-q4" (第四季度工资单文档) |
"view_payslips" (查看工资单) |
"doc-vendor-contracts" (供应商合同文档) |
"edit" (编辑) |
"doc-vendor-contracts" (供应商合同文档) |
"read" (读取) |
"folder-knowledge-base" (知识库文件夹) |
"read" (读取) |
"folder-payroll" (工资核算文件夹) |
"approve_payroll" (审批工资) |
通过图表追踪身份 bob 的所有访问路径,可以展示他被授予访问这些资源的途径。
5.2. 该身份可以访问哪些文档和文件夹?
返回身份可以访问的所有文档和文件夹,包括允许的操作以及授予这些权限的访问路径。
MATCH p=(i:Identity {id:$id})-->{1,}(e:Entitlement)-[:APPLIES_TO]->(r:Resource WHERE r.type IN ["folder","document"])
RETURN distinct r.id AS resource, r.type AS type, e.action AS entitlement
对于 $id=charlie,未找到结果:该身份是 IT 承包商,没有任何文件夹或文档的访问权限。切换到身份 id dana,结果显示如下:
| resource(资源) | type | 授权 (entitlement) |
|---|---|---|
"folder-knowledge-base" (知识库文件夹) |
"folder" (文件夹) |
"read" (读取) |
"doc-vendor-contracts" (供应商合同文档) |
"document" (文档) |
"read" (读取) |
"doc-vendor-contracts" (供应商合同文档) |
"document" (文档) |
"edit" (编辑) |
可视化路径表示清晰地描绘了这一点。
5.3. 谁可以访问此特定资源?
返回所有可以访问特定资源的身份,无论是通过继承还是直接访问。
MATCH p=(r:Resource {id:"doc-vendor-contracts"})<-[:APPLIES_TO]-(:Entitlement)<--{1,}(i:Identity)
RETURN distinct i.id AS identity
ORDER BY identity
结果
| 身份 (identity) |
|---|
"alice" |
"bob" |
"dana" |
5.4. 为什么该身份拥有此资源的访问权限?
显示所有解释该身份为何能访问特定资源的访问路径,包括非明显的或重叠的路线。
MATCH p=(r:Resource {id:"doc-vendor-contracts"})<-[:APPLIES_TO]-(:Entitlement)<--{1,}(i:Identity)
RETURN i.id AS identity, reverse([n in nodes(p) | COALESCE(n.id, n.action)]) AS accessPath
ORDER BY identity, size(accessPath)
结果
| 身份 (identity) | 访问路径 (accessPath) |
|---|---|
"alice" |
["alice", "group-ops", "group-emp", "role-content-reader", "read", "doc-vendor-contracts"] |
"bob" |
["bob", "group-proc-project", "role-content-editor", "read", "doc-vendor-contracts"] |
"bob" |
["bob", "group-proc-project", "role-content-editor", "edit", "doc-vendor-contracts"] |
"bob" |
["bob", "group-payroll", "group-fin", "role-content-editor", "read", "doc-vendor-contracts"] |
"bob" |
["bob", "group-payroll", "group-fin", "role-content-editor", "edit", "doc-vendor-contracts"] |
"bob" |
["bob", "group-payroll", "group-fin", "group-emp", "role-content-reader", "read", "doc-vendor-contracts"] |
"dana" |
["dana", "role-content-editor", "read", "doc-vendor-contracts"] |
"dana" |
["dana", "role-content-editor", "edit", "doc-vendor-contracts"] |
"dana" |
["dana", "group-emp", "role-content-reader", "read", "doc-vendor-contracts"] |
5.4. 最短解释路径
当存在多条路径时,最短解释路径提供了简洁、人类可读的访问原因,适用于审查和审计。
MATCH path = SHORTEST 1 (i:Identity {id:"bob"})-->{1,}(e:Entitlement {action:"edit"})-[:APPLIES_TO]->(r:Resource {id:"doc-vendor-contracts"})
RETURN path
身份 bob 访问供应商合同文档的最短访问路径是:
5.5. 哪些身份拥有直接角色分配?
识别拥有直接分配角色的身份,突出显示标准基于组分配之外的访问例外。
MATCH (i:Identity)-[:ASSIGNED_ROLE]→(r:Role) RETURN i.id AS identity, i.type AS identityType, r.id AS role, r.system AS system ORDER BY identity
在本例中,身份 dana 被授予了角色的直接访问权限,而无需从一个或多个组中继承。
| 身份 (identity) | 身份类型 (identityType) | role(角色) | 系统 |
|---|---|---|---|
"dana" |
"user" (用户) |
"role-content-editor" (内容编辑角色) |
"content" (内容) |
5.6. 哪些服务账户有权访问 IT 资产?
返回所有有权访问 IT 资产的服务账户,包括访问路径和允许的操作。
MATCH p=(i:Identity {type:
"service_account"})-->{1,}(e:Entitlement)-[:APPLIES_TO]->(r:Resource WHERE r.type IN ["server","application"])
RETURN distinct r.id AS resource, r.type AS type, e.action AS entitlement
| resource(资源) | type | 授权 (entitlement) |
|---|---|---|
"app-hr-portal" (人力资源门户应用) |
"application" (应用程序) |
"login" (登录) |
"server-fileshare-01" (服务器文件共享) |
"server" (服务器) |
"restart_service" (重启服务) |
5.7. 角色授予哪些授权?
返回角色授予的所有授权,显示该角色在资源上启用的操作。
MATCH (:Role {id: "role-content-editor"})-[:GRANTS]->(e:Entitlement)-[:APPLIES_TO]->(r:Resource)
RETURN r.id AS resource, r.type AS type, e.action AS entitlement
ORDER BY resource
结果
| resource(资源) | type | 授权 (entitlement) |
|---|---|---|
"doc-vendor-contracts" (供应商合同文档) |
"document" (文档) |
"read" (读取) |
"doc-vendor-contracts" (供应商合同文档) |
"document" (文档) |
"edit" (编辑) |
"folder-knowledge-base" (知识库文件夹) |
"folder" (文件夹) |
"read" (读取) |
5.8. 角色是如何在组织中继承的?
显示角色如何通过组成员身份继承,揭示哪些身份接收了该角色以及原因。
MATCH (r:Role {id: $id})
CALL(r) {
MATCH path=(r)<-[:GRANTS_ROLE]-(g:Group)<-[:MEMBER_OF]-{1,}(i:Identity)
RETURN path
UNION
MATCH path=(r)<-[:ASSIGNED_ROLE]-(i:Identity)
RETURN path
}
RETURN path
对于 role-content-reader 角色,访问是通过 Employees(员工)组获得的。
然而,role-content-editor 角色显示,该访问权限也直接授予了身份 dana。
6. 使用您自己的数据获取价值
此用例旨在清晰地映射大多数组织中已有的访问数据,无需更改现有的 IAM、目录或应用程序平台。
访问数据通常分布在熟悉的源中,包括身份提供商和目录、IAM 平台、SaaS 应用程序、文档管理系统和基础设施平台。
通过将现有用户、组、角色和访问权限映射到此模型中的身份、组、角色、授权和资源,组织可以立即开始分析有效访问。由于访问权限是通过遍历动态导出的,因此无需预先计算访问列表或在系统间协调策略。
团队可以从小规模开始——例如,从单个文档存储库或应用程序开始,并随着时间的推移逐步添加其他来源。随着新访问数据的添加,它可以使用相同的查询自动进行探索,而无需更改底层模型。
这种方法使组织能够使用熟悉的数据快速回答高价值的访问问题,同时随着访问需求的发展保持模型的简单和灵活。
7. 业务成果
通过将访问关系作为图进行建模和分析,组织可以获得关于“谁可以访问什么以及为什么”的清晰、可解释的视图——将访问权限从盲点转变为可衡量的安全控制。
与 CISO(首席信息安全官)优先事项一致的关键成果包括:
- 降低访问风险和攻击面
-
有效访问权限源于真实的关系,使得隐藏的、重叠的或意外的访问路径在被利用之前即可被发现。
- 更强的审计准备性和防御性
-
访问决策可以清晰、一致地解释,并提供完整的血缘关系,显示访问权限是如何授予的——支持更快的审计和更有信心的证明。
- 改进最小权限原则的执行
-
过度访问和不必要的继承变得清晰可见,从而能够在不干扰合法业务访问的情况下进行有针对性的补救。
- 更清晰地了解爆炸半径
-
安全团队可以通过了解身份、角色或组启用的确切访问权限,来评估受损资产的真实影响。
- 安全、IT 和业务之间更好的协调
-
可以以通俗易懂的语言进行推理和沟通,从而改善协作并减少利益相关者之间的摩擦。
- 可扩展的访问智能基础
-
同一模型随着时间的推移支持更高级的安全用例——包括访问优化、策略验证和跨域风险分析——而无需重新设计核心访问结构。
总之,这些成果有助于 CISO 从被动的访问报告转向主动的、可解释的访问控制——在提高安全态势的同时,适应现代组织的发展步伐。