c++怎么在Windows下利用ACL API设置文件的安全访问控制【底层】

酷墨同学_1936

酷墨同学_1936

2026-03-28

1043人浏览

原创

GetNamedSecurityInfo和SetNamedSecurityInfo是Windows底层ACL操作核心API,需手动构造EXPLICIT_ACCESS、正确处理NULL DACL、继承标志及UTF-8编码用户名,否则易现权限无效或访问拒绝。

c++怎么在windows下利用acl api设置文件的安全访问控制【底层】

GetNamedSecurityInfo 和 SetNamedSecurityInfo 是核心入口

Windows ACL 设置不走 CreateFile 或 SetFileAttributes,必须用 Advapi32.dll 提供的这两组 API。它们直接操作对象的安全描述符(SECURITY_DESCRIPTOR),绕过所有外壳层封装——这才是你想要的“底层”。GetNamedSecurityInfo 读取现有 DACL(或 SACL),SetNamedSecurityInfo 写入新 DACL;两者都要求你手动构造和释放 EXPLICIT_ACCESS 数组,不能只传个字符串权限。

常见错误现象:ERROR_INVALID_PARAMETER(传了空 ppsidOwner 却没设 OWNER_SECURITY_INFORMATION)、ERROR_ACCESS_DENIED(进程没“管理审核和安全日志”权限,或目标文件被其他进程独占打开)。

  • 必须用 SE_KERNEL_OBJECT 以外的类型调用:文件/目录用 SE_FILE_OBJECT,注册表用 SE_REGISTRY_KEY
  • 不要传 NULL 给 ppsidOwner/ppsidGroup —— 即使你不改 owner,也得先用 GetNamedSecurityInfo 拿到原值再传回去,否则写入失败
  • 调用前确保进程有 SE_TAKE_OWNERSHIP_NAME 权限(对非自己拥有的文件)或 SE_SECURITY_NAME(修改 SACL)——通常需以管理员身份运行

BuildExplicitAccessWithName 构造权限项时容易漏掉继承标志

用 BuildExplicitAccessWithName 快速生成 EXPLICIT_ACCESS 结构体很方便,但它默认不设任何继承标志。如果你要让子目录/文件自动继承权限,必须手动把 OBJECT_INHERIT_ACE 和 CONTAINER_INHERIT_ACE 或者两者都或(|)进 grfInheritance 字段——否则设置只生效于当前文件,新建的子项完全不受控。

使用场景:给一个项目根目录设置“开发组可读写、其他人只读”,且希望所有新增源码文件自动继承该策略。

  • grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE → 目录和文件都继承
  • grfInheritance = OBJECT_INHERIT_ACE → 只有文件继承(比如你只想控制 .cpp/.h,不希望子目录再套一层 ACL)
  • 别用 BuildExplicitAccessWithSid 替代——它不接受宽字符用户名,而 BuildExplicitAccessWithName 支持 L"DOMAIN\User" 格式,更贴近实际部署

SetNamedSecurityInfo 返回 ERROR_SUCCESS 但权限没变?检查 DACL 是否为 NULL

这是最隐蔽的坑:如果目标文件当前没有 DACL(即 DACL 为空指针),系统默认“拒绝所有访问”。此时你调用 SetNamedSecurityInfo 传入一个合法 DACL,API 返回 ERROR_SUCCESS,但资源管理器里看“安全”选项卡仍显示“无权限条目”。因为 Windows 把空 DACL 和“无 DACL”视为不同状态,而 SetNamedSecurityInfo 在 pDacl 非空时,会把原 DACL 替换为你传入的,但如果原对象压根没 DACL,你的新 DACL 就成了唯一来源——逻辑没错,但 UI 不刷新或缓存未清,容易误判失败。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

验证方法:用 GetNamedSecurityInfo 拿回 ppDacl,再调 GetAclInformation 看 ActualAceCount 是否大于 0。

  • 调用前先用 GetNamedSecurityInfo 拿原始 DACL,确认是否为 NULL;若为 NULL,构造新 DACL 时务必包含至少一条显式允许规则(如 GENERIC_READ 给 SYSTEM),否则写入后文件将彻底不可访问
  • 不要依赖资源管理器实时刷新——改完后用 icacls "path" /verify 命令行验证
  • 注意:SetNamedSecurityInfo 不支持原子性更新;如果中途失败,旧 DACL 已被覆盖,无法自动回滚

WideCharToMultiByte 转换用户名时编码必须用 CP_UTF8

虽然 Windows API 文档说 BuildExplicitAccessWithName 接受 ANSI 字符串,但实际在 Unicode 系统(所有现代 Windows)上,它内部会用当前 ACP 解码。一旦用户名含中文、emoji 或域账号(如 L"contoso\张三"),用默认 ACP(如 GBK)转换会导致乱码,最终 ACL 条目匹配失败——表现为“添加成功但用户仍无权限”。

正确做法是统一用 UTF-8 编码路径和用户名,并通过 WideCharToMultiByte(CP_UTF8, ...) 显式转出字节流。这和 SetNamedSecurityInfo 的文档行为不一致,但实测唯一稳定方案。

  • 不要用 std::string 存用户名再传给 API——C++ 字符串默认不保证 UTF-8,尤其 VS 默认是 MBCS
  • 所有宽字符串(LPWSTR)输入前,先用 WideCharToMultiByte(CP_UTF8, 0, wszName, -1, nullptr, 0, nullptr, nullptr) 测长度,再分配缓冲区
  • 若调用 SetNamedSecurityInfo 后返回 ERROR_INVALID_NAME,90% 是用户名编码问题,不是拼写错

ACL 不是开关按钮,它是多层嵌套结构:SID → ACE → DACL → SECURITY_DESCRIPTOR → 对象。每层都有自己的生命周期和所有权规则,漏掉一次 LocalFree 或多一次 HeapAlloc,都会让后续调用静默失败。别信“设完就完事”,得逐层验证。

大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

6719

3

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

2024.03.01

1698

6

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1798

5

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

2844

5

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6164

49

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2023.11.24

5949

6

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

2024.03.22

7554

16

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2024.04.29

11274

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习