File.GetAccessControl 默认仅返回当前用户有权查看的ACL片段,需传入AccessControlSections.Access获取完整DACL;IdentityReference常为SID,应try-translate为NTAccount;递归扫描须限制深度、过滤重解析点并避开系统路径;关键权限组合如Write+ExecuteFile及TakeOwnership需位运算检测,继承规则需追溯父级。

如何用 File.GetAccessControl 读取文件的 ACL 信息
直接调用 File.GetAccessControl 是最常用起点,但它默认返回的是当前用户有权限查看的那部分 ACL —— 如果你没管理员权限,很可能拿不到完整 FileSystemAccessRule 列表,甚至抛出 UnauthorizedAccessException。
实操建议:
- 务必用
try/catch包住调用,尤其在遍历目录时,跳过无权访问的路径比崩掉更合理 - 想获取完整 DACL(包括被继承/显式设置的规则),得传入
AccessControlSections.Access参数:File.GetAccessControl(path, AccessControlSections.Access) - 注意:.NET 5+ 默认启用“仅限安全上下文”模式,非管理员进程对系统路径(如
C:\Windows)基本读不到有效规则
为什么 FileSystemAccessRule 的 IdentityReference 常显示 SID 而不是用户名
因为 Windows ACL 存储的是 SID(安全标识符),不是名字。调用 .Value 直接输出就是 S-1-5-21-... 这种字符串,不解析根本看不出是谁。
实操建议:
- 用
identity.Translate(typeof(NTAccount)).Value转成可读名,但会抛IdentityNotMappedException—— 比如域账户离线、或本地机器查不到该 SID 的映射 - 生产环境别硬转,先
try Translate,失败就保留原始 SID,否则审计日志里一堆“无法解析的账户”反而干扰判断 - 检查是否属于内置组(如
BUILTIN\Administrators)时,别依赖字符串匹配,用IsWellKnown+WellKnownSidType更可靠
递归扫描时怎么避免权限爆炸和性能卡死
对整个 C:\ 或网络共享跑 GetAccessControl,轻则几小时,重则触发 SMB 超时或本地句柄耗尽 —— 不是代码写错了,是 Windows 底层开销真这么大。
实操建议:
- 跳过符号链接和挂载点:
FileAttributes.ReparsePoint要过滤,否则可能陷入循环或跨卷失控 - 限制深度:用计数器控制递归层级,超过 8 层就停,默认策略通常不关心嵌套太深的子目录
- 批量跳过已知安全路径:比如
%SystemRoot%\Temp、%LocalAppData%这类本就不该设严格权限的地方,提前Continue - 别在 UI 线程做全盘扫描,后台线程 + 进度回调 + 可取消
CancellationToken是底线
FileSystemRights 的位运算容易漏掉哪些关键权限组合
很多人只检查 FileSystemRights.FullControl 或 Modify,但真正危险的是 Write + ExecuteFile 组合 —— 它允许改脚本并立刻执行,比单纯 FullControl 更隐蔽。
实操建议:
- 用
&位与判断是否包含某权限:(rule.FileSystemRights & FileSystemRights.ExecuteFile) == FileSystemRights.ExecuteFile - 特别关注
TakeOwnership和ChangePermissions:这两个权限一旦被非管理员持有,整条权限链就不可信 - 继承标志别忽略:
rule.IsInherited为false才是显式设置,才是策略审计的重点目标;继承来的规则要追溯父级,不能只看当前节点
ACL 解析本身不难,难的是在权限缺失、SID 失效、路径循环、策略分层这些现实约束下,让结果既准确又可控。绕不开的点只有一个:永远假设你拿不到全部信息,然后决定哪些缺失可以接受,哪些必须报错。










