Windows 服务帐户选定 ── LocalSystem、虚拟帐户与 gMSA
· Go Komura · Windows, Windows 服务, 服务帐户, gMSA, LocalSystem, 虚拟帐户, 安全, Active Directory, 最小权限
“我们‘暂时’用 LocalSystem 跑的内部服务,在安全审计被点名‘权限过大’。该改成什么?”“服务访问不到共享文件夹,所以用域用户在跑。密码到期服务就停,于是设成永不到期,还把明文写进操作手册。”── 客户 Windows 服务相关咨询里,这两则是固定班底。
两边现场的共通点是:服务的登录帐户被冻成“碰巧能跑的设置”,而不是设计决策。Windows 服务一定在某个帐户的安全上下文中运行,那个帐户决定它本地能做什么、从网络另一端看起来是谁、密码由谁管理。把这里留在默认值,单个服务的弱点就直接变成整台机器被接管,明文密码也散进操作手册与脚本。
flowchart TB
accTitle: 登录帐户决定的三件事
accDescr: 服务一定在某个帐户的安全上下文中运行,该帐户决定本地能做什么、从网络另一端看起来是谁、密码由谁管理
acct["服务的登录帐户"] --> local["本地能做什么"]
acct --> net["从网络另一端看起来是谁"]
acct --> pwd["密码由谁管理"]
图 1: 选定登录帐户是同时决定本地权限、网络身份与密码管理的设计决策。
实务上有六种选择 ── LocalSystem、LocalService、NetworkService、虚拟帐户(NT SERVICE\<服务名称>)、域用户,以及 gMSA(组托管服务帐户)。本文面向中小企业信息化人员与 Windows 应用程序开发者,把这六种的权限、网络身份与密码管理整理成一张表,并依 2026 年 8 月当下的 Microsoft Learn 一手资料整理到判断流程。服务名称>
服务本身怎么做(与任务计划程序的取舍、用 .NET Worker Service 实现)见“Windows 服务的创建与运维”。本文集中在出事最多的“登录帐户”。
1. 先讲结论
- 拿不定主意时,在单台机器内做完的服务以虚拟帐户为第一候选;要以服务专属身份访问域内资源的服务以 gMSA 为第一候选。 Microsoft 也指导尽可能使用托管帐户(MSA / 虚拟帐户)。12
- 不要因为“能跑”就选 LocalSystem。 令牌含 SYSTEM 与 BUILTIN\Administrators,并持有 SeDebugPrivilege 等强特权,被接管就几乎失去该机器上的一切。
sc.exe create默认是 LocalSystem,正是这类事故的温床。34 - LocalService 与 NetworkService 的差别是网络身份。 本地权限两者都最小,但对远程来说 LocalService 是匿名,NetworkService 是计算机帐户。5
- **虚拟帐户(NT SERVICE\<服务名称>)是现代默认:不必管密码,又能按服务分开身份。** 可在 ACL 上直接指定“NT SERVICE\\服务名称”,SQL Server 的默认服务帐户也是这个。[^understand-service-accounts][^sql-service-accounts]服务名称>
- LocalSystem、NetworkService 或虚拟帐户走到网络上时,会变成计算机帐户(DOMAIN\计算机名$)。 在共享文件夹或 SQL Server 的 ACL 授予 PC$,常常就能不用域用户。36
- 把域用户用在服务上,会在密码运营与 Kerberoasting 两边都变成负债。 SCM 用保存的密码登录,到期就启动失败;为了避开而采用“永不到期 + 明文备忘”等于送给攻击者。78
- gMSA 由 Active Directory 自动生成并轮换密码。 要件是域与 KDS 根密钥,服务设成“DOMAIN\帐户名$”且密码栏留空。有些应用程序不支持,需事先验证。910
- 改帐户会改掉配置文件、%TEMP%、DPAPI 的前提。 旧帐户 DPAPI 保护的数据,新帐户解不开。
- 现况盘点可由服务列表的登录帐户,以及事件 ID 4624(登录类型 5)确认。11
一句话总结,本文结论是:把“不把人类密码交给服务”的配置(内置帐户、虚拟帐户、gMSA)当默认,域用户当最后手段。
2. 选项全貌 ── 六种登录帐户一张表
先复习一层前提。服务启动时,服务控制管理器(SCM)以配置的帐户登录,成功后创建访问令牌并指派给服务进程。之后文件、管道等一切资源访问,都靠这个令牌与 ACL 比对来判定。7 所以选定登录帐户就是 决定交给服务进程的令牌内容的设计。以下列出六种选择。
flowchart TB
accTitle: 服务启动时 SCM 做的事
accDescr: SCM 以配置的帐户登录,成功后创建访问令牌并指派给服务进程,之后资源访问由令牌与 ACL 比对决定
scm["SCM"] --> logon["以配置的帐户登录"]
logon --> token["创建访问令牌"]
token --> proc["指派给服务进程"]
proc --> access["访问文件或管道"]
access --> check{"ACL 允许吗?"}
check -->|是| ok["访问成功"]
check -->|否| deny["访问被拒"]
图 2: 服务的每一次资源访问,都由启动时 SCM 创建的令牌与 ACL 比对决定。
| 帐户 | 本地权限 | 网络身份 | 密码管理 | 典型用途 |
|---|---|---|---|---|
| LocalSystem | 几乎无限制(SYSTEM+Administrators) | 计算机帐户(PC$) | 不需要(无密码) | 与操作系统一体运行的例外服务 |
| LocalService | 最小(Users 级) | 匿名 | 不需要 | 不需要网络身份的本地处理 |
| NetworkService | 最小(Users 级) | 计算机帐户(PC$) | 不需要 | 机器级身份就够用的低权限处理 |
| 虚拟帐户 NT SERVICE\<名称>名称> | 最小 + 在 ACL 个别授予 | 计算机帐户(PC$) | 不需要(自动管理) | 跑在单台服务器上的业务服务默认 |
| 域用户 | 只限你授予的 | 该用户本身 | 手动(到期、泄露、轮换都交给人) | 不支持 gMSA 的应用程序的最后手段 |
| gMSA | 只限你授予的 | 该 gMSA 本身 | AD 自动生成并轮换 | 域环境需要服务专属身份时 |
LocalSystem、LocalService、NetworkService、虚拟帐户都 完全没有密码这个概念。用 SCM 保存的密码登录(= 可能到期与泄露)的只有域用户与本地用户。73
下面按行逐一深入。
3. LocalSystem 有什么问题
3.1. 比“以管理员身份运行”更强
LocalSystem(显示名称 Local System,NT AUTHORITY\SYSTEM)是 SCM 使用的预定义帐户,在本地计算机持有广泛权限。令牌含 NT AUTHORITY\SYSTEM 与 BUILTIN\Administrators 的 SID,可访问系统上大多数对象。此外,可调试其他进程的 SeDebugPrivilege,以及作为操作系统一部分运行的 SeTcbPrivilege,默认就已启用。3
这份强大等同于被接管时的伤害规模。以 LocalSystem 运行的服务只要有一个任意代码执行弱点,攻击者一口气就能读改该机器上所有用户的文件(SYSTEM 在 NTFS 默认具有完全控制5)、用 SeDebugPrivilege 读其他进程内存,再从那里窃取凭据并横向移动(Pass-the-Hash 等的起点)。凭据窃取与横向移动的链条见“图解 NTLM 与 Kerberos”与“Windows LAPS实务指南”。
flowchart TB
accTitle: LocalSystem 服务被接管时的伤害
accDescr: 以 LocalSystem 运行的服务只要有一个任意代码执行弱点,攻击者就能读改所有用户文件、读其他进程内存,并窃取凭据后横向移动
vuln["一个任意代码执行弱点"] --> sys["攻击者取得 SYSTEM 权限"]
sys --> files["读取与篡改文件"]
sys --> mem["读其他进程内存"]
sys --> cred["窃取凭据"]
cred --> lateral["横向移动到另一台机器"]
图 3: LocalSystem 服务的一个弱点,就能一口气走到整机接管与横向移动的起点。
3.2. 为什么还是被选
理由很单纯:它是默认值,而且不会出现访问被拒。sc.exe create 省略 obj= 时默认是 LocalSystem,4 许多旧示例代码与安装程序模板仍假设 LocalSystem。开发期间可以远离权限错误,于是大量产出“能跑就先这样”的结构。Microsoft 自己的文档也写道,多数服务不需要这么高的权限级别,若不需要就应考虑 LocalService 或 NetworkService。3
flowchart TB
accTitle: LocalSystem 持续被选的结构
accDescr: sc.exe create 默认是 LocalSystem,旧示例与模板也假设 LocalSystem,开发时不会出现访问被拒,于是大量产出能跑就先这样的配置
def["sc.exe create 的默认"] --> lsys["建成 LocalSystem"]
old["旧示例与模板"] --> lsys
lsys --> noerr["开发时没有访问被拒"]
noerr --> asis["能跑就先这样"]
asis --> mass["权限过大的服务被量产"]
图 4: 默认值加上“没有访问被拒”的开发体验,量产了冻在 LocalSystem 的服务。
3.3. 与 TrustedInstaller 的差别 ── LocalSystem 也不是无限
把 LocalSystem 叫成“Windows 最强帐户”并不准确。Windows Vista 起的 Windows 资源保护(WRP)只允许 TrustedInstaller(Windows Modules Installer 服务)更改重要的操作系统系统文件、文件夹与注册表项,连 SYSTEM 或管理员改写都会访问被拒。12 Explorer 的“您需要来自 TrustedInstaller 的权限”就是这个机制。反过来说,LocalSystem 几乎能碰到 WRP 保护区以外的一切,通常没有理由把这个给业务服务。
flowchart TB
accTitle: WRP 保护区与 TrustedInstaller 的关系
accDescr: WRP 保护的重要系统文件与注册表项,只允许 TrustedInstaller 更改,连 SYSTEM 或管理员都会访问被拒
ti["TrustedInstaller"] -->|可以更改| wrp["WRP 保护的系统文件等"]
sysadm["SYSTEM 与管理员"] -->|访问被拒| wrp
sysadm -->|几乎全部允许| other["WRP 保护区以外"]
图 5: LocalSystem 也不是无限;WRP 保护区的更改只允许 TrustedInstaller。
3.4. LocalSystem 合理的情况
例外合理的是 所需权限本来就超过管理员级 的服务 ── 与设备驱动程序紧密配合、操作操作系统安全基础、管理其他服务或会话等。备份代理或 EDR 这类软件属此。即便如此,仍值得确认是否真有用到该权限的代码路径,并考虑能否把需要权限的工作分开(怎么分辨见“Windows 什么时候需要管理员权限”)。
4. LocalService 与 NetworkService ── 最小权限的内置帐户
LocalService(NT AUTHORITY\LOCAL SERVICE,SID: S-1-5-19)与 NetworkService(NT AUTHORITY\NETWORK SERVICE,SID: S-1-5-20)是为低权限服务准备的内置帐户。两者在本地都只有最小权限,能做的事比 Users 组成员多不了多少。51
两者差别只有一点:从网络另一端看起来是谁。5
- LocalService:以 匿名凭据 连到远程。无法访问需要身份验证的资源。
- NetworkService:向远程出示 计算机的凭据(域环境为 DOMAIN\计算机名$)。
切分是“不上网络,或上了也不需要身份”用 LocalService,“要以机器身份访问域内资源”用 NetworkService。
flowchart TB
accTitle: LocalService 与 NetworkService 的差别
accDescr: 本地权限两者都最小,但对远程 LocalService 以匿名凭据连接,NetworkService 出示计算机的凭据
ls["LocalService"] --> anon["以匿名凭据连接"]
anon -.-> ng["需要身份验证的资源做不到"]
ns["NetworkService"] --> comp["出示计算机的凭据"]
comp -.-> pc["域环境看起来像 PC$"]
图 6: 本地权限同为最小,但从网络另一端看到的身份分成匿名或计算机帐户。
不过从现代观点,这两者有弱点。同一个帐户被许多服务共用。 若五个服务都以 LocalService 运行,只要 ACL 是按帐户,五个就能互相访问对方的资源。SQL Server 不支持 Local Service 帐户也是同一理由:它是共用帐户,无法与其他服务分开。1
flowchart TB
accTitle: 共用帐户无法分开
accDescr: 若多个服务共用同一个 LocalService,只要 ACL 按帐户,就能互相访问对方的资源
sva["服务 A"] --> acct["同一个 LocalService"]
svb["服务 B"] --> acct
svc["服务 C"] --> acct
acct --> mutual["能互相访问对方的资源"]
mutual -.-> reason["因为 ACL 是按帐户"]
图 7: 共用同一帐户的服务,无法用 ACL 把彼此的资源分开。
解决“维持低权限,但按服务分开”的,就是下一题的虚拟帐户。
5. 虚拟帐户(NT SERVICE\<服务名称>)── 现代默认服务名称>
5.1. 不必密码就能有每服务身份
虚拟帐户是 Windows Server 2008 R2 / Windows 7 起可用的“托管本地帐户”。特性有三。6
- 帐户 自动管理;不必创建也不必设密码
- 名称是
NT SERVICE\<服务名称>,成为 每个服务独有的身份 - 在域环境可用 计算机帐户的凭据(DOMAIN\计算机名$) 访问网络
也就是保住 LocalService/NetworkService“不必管密码”的优点,拿掉“帐户共用所以分不开”的缺点。SQL Server 安装默认用 NT SERVICE\MSSQLSERVER 这类虚拟帐户,也是这个原因。1
flowchart TB
accTitle: 虚拟帐户让什么得以并存
accDescr: 虚拟帐户保住 LocalService 与 NetworkService 不必管密码的优点,拿掉因共用而分不开的缺点,并具备每个服务独有的身份
merit["优点(不必管密码)"] -->|保住| va["虚拟帐户"]
demerit["缺点(共用而分不开)"] -->|拿掉| va
va --> ident["每个服务独有的身份"]
va --> auto["不必创建也不必设密码"]
图 8: 虚拟帐户保住内置帐户的优点,只拿掉因共用而分不开的缺点。
5.2. 可在 ACL 上直接写“NT SERVICE\服务名称”
实务上的方便是 可以按名称只把该服务加进 ACL。“只有这个服务能写这个数据文件夹”不必建组也不必管密码就能做到。
# Change the service's logon account to a virtual account
# The value of obj= is "NT SERVICE\service-name". Do not specify a password
sc.exe config MyAppService obj= "NT SERVICE\MyAppService"
# Confirm the configuration (check SERVICE_START_NAME)
sc.exe qc MyAppService
# Grant modify rights on the data folder to this service only
icacls "C:\ProgramData\MyApp" /grant "NT SERVICE\MyAppService:(OI)(CI)M"
在 GUI 里,于 services.msc 打开服务属性 →“登录”选项卡 → 在“此帐户”输入 NT SERVICE\服务名称,密码栏留空(虚拟帐户或 MSA 不指定密码是 SCM 的规格)。更改后重新启动服务即应用。
5.3. 限制 ── 出了机器就不是“那个服务”
虚拟帐户的身份是机器本地的,域认不出来。到了网络上会如后文所述缩成计算机帐户,所以 远程分不出“是哪个服务”,也不能在多台服务器共用同一身份。10
flowchart TB
accTitle: 虚拟帐户的身份在机器外会缩水
accDescr: 在机器内每个服务独有的虚拟帐户,到了网络上也缩成计算机帐户,远程分不出是哪个服务
vaa["虚拟帐户 A"] --> pc["计算机帐户 PC$"]
vab["虚拟帐户 B"] --> pc
pc --> remote["远程看到的身份"]
remote -.-> nodist["分不出是哪个服务"]
图 9: 即使机器内身份独有,在网络另一端每个服务看起来都是同一个 PC$。
这个限制 ── 需要在网络另一端有服务专属身份、需要在多台服务器共用同一身份 ── 成为问题的那一刻,就是该叫出 gMSA(第 8 章)的时候。
6. 走上网络时的身份 ── 计算机帐户(PC$)的实务
6.1. “服务访问不到共享文件夹”是误解
在已加入域的机器上,以 LocalSystem、NetworkService 或虚拟帐户运行的服务访问远程资源时,会以计算机帐户(DOMAIN\计算机名$)验证。36 开头那些“访问不到共享文件夹所以改成域用户”的咨询,其实多半这样就解了。只是目标端 ACL 没有允许 PC$。
flowchart TB
accTitle: 以计算机帐户进行远程访问
accDescr: 已加入域的机器上,LocalSystem、NetworkService 或虚拟帐户服务以计算机帐户向远程验证,目标端 ACL 允许 PC$ 就能访问
svc["服务(LocalSystem、虚拟帐户等)"] --> auth["以 PC$ 验证"]
auth --> acl{"目标端 ACL 允许 PC$ 吗?"}
acl -->|是| ok["共享文件夹或数据库访问成功"]
acl -->|否| ng["访问被拒"]
图 10: 在域环境,只要在目标端 ACL 授予 PC$,不必域用户也能建立远程访问。
文件服务器端的授予与一般 ACL 操作相同;帐户名指定 计算机名$(GUI 对象选择对话框请把对象类型含“计算机”)。
# On the file-server side: grant a service on APPSV01 modify rights on the shared folder
# You need to grant both share permissions and NTFS permissions
Grant-SmbShareAccess -Name "AppData" -AccountName "CORP\APPSV01$" -AccessRight Change -Force
icacls "D:\Shares\AppData" /grant "CORP\APPSV01$:(OI)(CI)M"
SQL Server 也一样:把计算机帐户建成登录,连接字符串用 Integrated Security=true 即可,不必密码。
-- On the DB-server side: permit Windows integrated authentication from a service on APPSV01
CREATE LOGIN [CORP\APPSV01$] FROM WINDOWS;
6.2. 认清 PC$ 做法的极限
这种做法有两个极限。
- 粒度是整台机器。 同一机器上的 LocalSystem、NetworkService 与所有虚拟帐户服务,从远程看起来都是同一个 PC$。无法在目标端“只允许这个服务”,也无法审计是哪个服务用了该帐户。2
- 工作组环境不能用。 计算机帐户是 Active Directory 对象,未加入域的机器没有。需要显式处理目标端帐户凭据的设计。
想越过极限 1 时,2026 年的答案不是下一章的域用户……而是跳过那个问题,直接走向 gMSA。
flowchart TB
accTitle: PC$ 做法的两个极限
accDescr: 以 PC$ 验证的粒度是机器级,无法做每服务允许或审计;工作组环境没有计算机帐户本身,因此不能用
pcs["PC$ 做法"] --> lim1["极限 1:整台机器"]
pcs --> lim2["极限 2:没有工作组"]
lim1 -.-> noaudit["没有每服务允许或审计"]
lim2 -.-> nocred["使用显式凭据"]
lim1 --> gmsa["越过这里:gMSA"]
图 11: 想越过机器级粒度与域前提这两个极限时,跳过域用户,走向 gMSA。
7. 把域用户用在服务上的问题
7.1. 密码的结构性问题
若把域用户(或本地用户)指派给服务,SCM 会保存该密码,每次启动都用它登录。SCM 不管到期,所以 密码到期登录就失败,服务不会启动。7
从那里开始,现场常见的负向螺旋出现。
- 发生因到期而服务停止的事故
- 为防再发,设成“密码永不到期”
- 更改程序从未建立,同一密码以明文写进多台服务器的操作手册、脚本与任务计划程序
- 即使有人离职也不改密码(一改就不知道什么会停)
flowchart TB
accTitle: 以域用户运营的负向螺旋
accDescr: 密码到期服务停止,为防再发设成永不到期,明文密码散进操作手册与脚本,即使有人离职也改不了
expire["1. 到期让服务停止"] --> forever["2. 为防再发设成永不到期"]
forever --> spread["3. 明文密码扩散"]
spread -.-> where["操作手册、脚本、任务"]
spread --> stuck["4. 即使有人离职也改不了"]
图 12: 从到期事故开始,永不到期与明文密码扩散就此固定。
Microsoft 也指出,服务使用域帐户的配置,在密码与 SPN 的手动管理上耗费可观运营力气,维护还可能导致服务停止。1
7.2. Kerberoasting ── 服务帐户成为目标
域用户服务帐户特有的另一种攻击是 Kerberoasting。接受 Kerberos 身份验证的服务会在登录帐户上注册 SPN(服务主体名称)。域内任何已验证用户都能向已注册 SPN 的帐户请求服务票据,攻击者取得票据后离线暴力破解密码。人类决定的 10 到 16 字符密码挡不住这次攻击。
flowchart TB
accTitle: Kerberoasting 的流程
accDescr: 对已注册 SPN 的服务帐户的服务票据,任何已验证用户都能请求,攻击者取得票据后离线暴力破解密码
atk["域内已验证的用户"] --> req["请求该 SPN 的票据"]
req --> tkt["取得服务票据"]
tkt --> brute["离线暴力破解"]
brute --> weak["大约 10 到 16 字符就会被破解"]
图 13: 任何已验证用户都能请求票据,人类决定长度的密码挡不住离线暴力破解。
有效对应是 把密码做成人类猜不到也破解不了的强度。Microsoft 也列出强制长密码,以及使用密码为机器生成长随机值的 gMSA。8 同一文档也提到 Kerberos 装甲(FAST),但 FAST 保护预身份验证数据与对抗 KDC 欺骗;它并不阻止已验证用户向 SPN 请求服务票据,因此不能替代服务帐户的密码强度。SPN 与 Kerberos 的关系、身份验证回退到 NTLM 的条件,图解于“图解 NTLM 与 Kerberos”。
7.3. 若仍使用域用户
若因应用程序不支持 gMSA 等原因别无选择,把以下当作最低限度的缓解。
- 把密码做成随机生成的 25 字符以上,除密码管理工具外(操作手册、脚本、共享 Excel)哪里都不写
- 做成服务专用帐户并按服务拆开(不要与人类帐户共用2)
- 拒绝交互式登录与远程桌面,只允许“作为服务登录”
- 所属组最小化(加入 Domain Admins 想都别想)
- 建立定期轮换程序,把更改会影响的地方列入清册
全部做完仍比迁到 gMSA 更不安全也不更省事 ── 那就是下一章。
8. gMSA ── 把密码管理交给 Active Directory
8.1. 机制与效果
gMSA(组托管服务帐户)是把密码管理交给域控制器的域帐户。密码由域控制器从 KDS(Key Distribution Service)根密钥计算,只有获准的主机能取得。13
flowchart TB
accTitle: gMSA 如何管理密码
accDescr: 域控制器从 KDS 根密钥计算密码,只有获准的主机取得并用来运行服务,密码默认每 30 天自动轮换
kds["KDS 根密钥"] --> dc["DC 计算密码"]
dc --> host["获准的主机取得"]
host --> svc["用来运行服务"]
dc -.-> rot["默认每 30 天自动轮换"]
图 14: 域控制器承担生成、分发与更新密码,人可以在不知道密码的情况下运营。
效果很清楚。9
- 240 字节随机生成的密码:暴力破解与字典攻击变得不实际,Kerberoasting 抗性大幅上升
- 默认每 30 天自动轮换:人不需要规划更改,服务也不需要停
- 可在多台服务器共用同一身份:负载均衡下的服务器场能以同一主体互相验证
- 更单纯的 SPN 管理:SPN 的注册与管理也可委派并简化
人可以在不知道密码的情况下运营 ── 若把它理解成 Windows LAPS 对本地管理员密码所做的事,套到服务帐户上的机制,位置就比较好抓。
8.2. 要件
gMSA 有前提。10
- Active Directory 域环境(工作组做不到)
- 域与林功能级别为 Windows Server 2012 或更高
- KDS 根密钥已经创建
- gMSA 名称在林内唯一,不只是域内
- 密码更改间隔只能在创建时设置
创建 KDS 根密钥是一次性工作,但 创建后最多 10 小时不能创建 gMSA,因为要等复制到每一台域控制器。这是防止复制尚未完成就取密码失败的安全装置。14
flowchart TB
accTitle: 从创建 KDS 根密钥到创建 gMSA
accDescr: 创建 KDS 根密钥后要等复制到每一台域控制器,因此最多 10 小时不能创建 gMSA;复制完成后才能创建
add["创建 KDS 根密钥"] --> wait["最多 10 小时等待复制"]
wait -.-> why["防止取回失败事故的安全装置"]
wait --> done["已复制到每一台 DC"]
done --> ok["可以创建 gMSA"]
图 15: 创建根密钥后最多 10 小时的等待,是为了避免复制尚未完成就取回失败。
# Run as a domain administrator, on a domain controller (or an administrative
# workstation with the AD PowerShell module)
# Confirm whether a KDS root key exists, and create one if not (once per forest)
Get-KdsRootKey
Add-KdsRootKey -EffectiveImmediately # Actually usable after up to 10 hours
8.3. 从创建到配置的程序
程序分四阶段:“① 创建获准取回的组 → ② 创建 gMSA → ③ 安装到服务器 → ④ 设置到服务”。10
flowchart TB
accTitle: 引入 gMSA 的四个阶段
accDescr: 以四个阶段引入:创建获准取回密码的组、创建 gMSA、安装到各服务器、设为服务的登录帐户
st1["① 创建获准取回的组"] --> st2["② 创建 gMSA"]
st1 -.-> add["加入服务器的 PC$"]
st2 --> st3["③ 安装到各服务器"]
st3 -.-> test["用 Test 命令验证取回"]
st3 --> st4["④ 设置到服务"]
图 16: 从创建组到设置服务,引入 gMSA 分四个阶段进行。
# ① Create a security group permitted to retrieve the password,
# and add the computer accounts of the servers that will run the service
New-ADGroup -Name "GG-SvcBatchHosts" -GroupScope Global
Add-ADGroupMember -Identity "GG-SvcBatchHosts" -Members "APPSV01$", "APPSV02$"
# Group membership is evaluated at computer logon, so
# restarting the target servers after adding is the reliable approach
# ② Create the gMSA
New-ADServiceAccount -Name "svc-batch" `
-DNSHostName "svc-batch.corp.example.com" `
-PrincipalsAllowedToRetrieveManagedPassword "GG-SvcBatchHosts"
# ③ On each server that will run the service, install the gMSA and validate
Install-ADServiceAccount -Identity "svc-batch"
Test-ADServiceAccount -Identity "svc-batch" # True means retrieval is working
# ④ Set it as the service's logon account. Append $ to the name, and do not specify a password
sc.exe config MyBatchService obj= "CORP\svc-batch$"
Restart-Service MyBatchService
从 services.msc 设置时,帐户名也像 CORP\svc-batch$ ── 结尾加上 $,密码栏留空。MSA 系列帐户不能用于交互式登录。1 之后在共享文件夹或 SQL Server 的 ACL 授予 CORP\svc-batch$ 取代 PC$,以服务专属身份的网络访问就完成了,而且不必密码。
8.4. 有些应用程序不支持
要注意的是,不是每套软件都能以 gMSA 运行。通过标准机制设置登录身份的 ── Windows 服务、IIS 应用程序池、任务计划程序任务 ── 支持面很广,但也有限制,例如故障转移群集本身不支持 gMSA,以及内部要求密码的应用程序不能用。10 Microsoft 也明白写道,正式上线前应在测试环境确认以 gMSA 运行的行为。9
flowchart TB
accTitle: 如何判断是否支持 gMSA
accDescr: 通过标准机制设置登录身份的应用程序广泛支持 gMSA,但故障转移群集与内部要求密码的应用程序不能用,因此正式上线前要在测试环境确认
app["目标应用程序"] --> how{"登录怎么设置?"}
how -->|标准机制| okapp["支持 gMSA"]
okapp -.-> ex1["服务、IIS、任务"]
how -->|要求密码| ngapp["不能用 gMSA"]
ngapp -.-> ex2["故障转移群集"]
okapp --> test["正式上线前测试"]
图 17: 通过标准机制设置登录的应用程序广泛支持,但也有不支持的设计,正式上线前的验证不可或缺。
还有兄弟帐户:单台服务器的 sMSA(独立托管服务帐户),以及 Windows Server 2025 引入、绑定设备身份以对抗凭据窃取的 dMSA(委派托管服务帐户)。新建时以 gMSA 为基准,再按需求考虑。6
9. 随附设计 ── 登录权利、配置文件、DPAPI、审计
还有四件会随帐户改变的事要记住。
9.1. “作为服务登录”权利(SeServiceLogonRight)
要以服务启动,帐户需要“作为服务登录”用户权利。LocalSystem、LocalService、NetworkService 内置就有,但 其他帐户(域用户、gMSA 等)需要显式指派。15
若从 services.msc GUI 的“登录”选项卡设置,管理单元会自动授予此权利。另一方面,CreateService / ChangeServiceConfig(sc.exe config 调用的 API)并不验证指定帐户是否有此权利。用脚本配置的服务在启动时以“因登录失败而无法启动服务”停下,典型原因就是这个。不要依赖工具的副作用;把显式加入本地安全策略(secpol.msc)的“作为服务登录”,或通过 GPO/Intune 配置,写进部署程序(若此权利由组策略配置,策略应用时会覆盖本地授予,这一点也要注意)。反过来说,服务专用帐户的标准动作是一并设置“拒绝本地登录”。
flowchart TB
accTitle: “作为服务登录”权利因配置路径而异
accDescr: services.msc GUI 会自动授予权利,但 sc.exe config 调用的 API 不验证权利,没有权利的帐户会在启动时因登录失败而停下
gui["在 services.msc 设置"] --> auto["权利自动授予"]
auto --> okgui["服务可以启动"]
cli["用 sc.exe config 设置"] --> noval["不验证权利"]
noval --> has{"有权利吗?"}
has -->|是| okcli["服务可以启动"]
has -->|否| stop["因登录失败停下"]
stop -.-> fix["用 secpol.msc 或 GPO 显式授予"]
图 18: GUI 会自动授予权利,但脚本配置不验证,因此程序里必须包含显式授予。
9.2. 配置文件、%TEMP%、HKEY_CURRENT_USER 会变
SCM 在服务启动时加载该帐户的用户配置文件。7 因此实际的 %TEMP%、%APPDATA%、HKEY_CURRENT_USER 依登录帐户而不同,切换帐户后,旧帐户配置文件里存的设置与缓存看起来像“消失了”。
设计上的对应很单纯:把服务数据不要放在配置文件底下,而是放在 C:\ProgramData\<应用程序名称> 这类明确路径,并把该 ACL 授予登录帐户。这样帐户变更就不必伴随数据迁移。
flowchart TB
accTitle: 配置文件依赖与数据放置的对应
accDescr: 实际配置文件依登录帐户而不同,切换帐户会让旧配置文件数据看起来像消失,但放在明确路径并授予 ACL 就不必迁移
sw["切换登录帐户"] --> newprof["加载不同的配置文件"]
newprof --> lost["旧数据看起来像消失"]
lost -.->|对应| fix["放到 ProgramData 底下"]
fix --> acl["把 ACL 授予登录帐户"]
acl --> nomig["帐户变更也不必迁移"]
图 19: 避开配置文件、把数据放在明确路径,帐户变更就不再伴随数据迁移。
9.3. 以 DPAPI 保护的数据绑在帐户上
更容易漏掉的是 DPAPI。以用户范围 DPAPI(CryptProtectData 或 .NET 的 ProtectedData)加密的数据,原则上只有当初保护它的同一个帐户才能解密。一改帐户,存好的连接字符串或 API 密钥就读不到 ── 那是 DPAPI 在正确做事,但若不在迁移程序里就会变成事故。
flowchart TB
accTitle: DPAPI 保护数据与帐户变更的关系
accDescr: 以用户范围 DPAPI 保护的数据只能由当初保护它的同一个帐户解密,因此更改登录帐户后需要重新输入机密
protect["用旧帐户做 DPAPI 保护"] --> data["受保护的连接字符串等"]
data --> who{"哪个帐户在解密?"}
who -->|同一个旧帐户| okdec["能解密"]
who -->|新帐户| ngdec["不能解密"]
ngdec --> re["重新输入机密"]
图 20: DPAPI 保护的数据绑在当初保护它的帐户上,帐户切换后需要重新输入。
对应是把“帐户切换后重新输入机密”写进迁移计划(存储在哪里的设计见“Windows 应用程序不要把敏感信息以明文存进配置文件的最佳实践”)。此外,能用 gMSA 或 PC$ 以 Windows 集成身份验证做完的配置,可以根本不必存储机密。正确顺序是先考虑“能否不存”,再考虑“存在哪里”。
若服务想“以调用者的权限”处理,不要把帐户变得更强,而是用模拟。那见“正确处理 Windows 的模拟令牌”。
9.4. 审计 ── 看 4624 登录类型 5
服务启动会以事件 ID 4624(帐户已成功登录)的 登录类型 5(Service:SCM 启动了服务) 写入安全事件日志。事件中的“Virtual Account”字段指出登录是否由 MSA / 虚拟帐户进行,也可用来监视托管帐户的使用。11
flowchart TB
accTitle: 审计服务启动的流程
accDescr: SCM 启动服务会记录为事件 ID 4624 登录类型 5,Virtual Account 字段可识别是否为托管帐户登录
start["SCM 启动服务"] --> ev["记录事件 ID 4624"]
ev --> type5["登录类型 5(Service)"]
type5 --> vafield["Virtual Account 字段"]
vafield --> watch["监视托管帐户"]
图 21: 服务启动记录为登录类型 5 的 4624,连托管帐户的使用都能跟踪。
现况盘点的快方法是汇总服务列表的登录帐户。
# Aggregate which services are running as which account
Get-CimInstance Win32_Service |
Group-Object StartName |
Sort-Object Count -Descending |
Select-Object Count, Name
# Inventory non-standard services running as LocalSystem (tell in-house / third-party by the path)
Get-CimInstance Win32_Service |
Where-Object { $_.StartName -eq 'LocalSystem' -and $_.PathName -notlike '*\Windows\*' } |
Select-Object Name, DisplayName, PathName
若这份输出排着“以 LocalSystem 运行的业务服务”与“以域用户运行的服务”,就该叫出下一章的判断流程。
10. 判断流程 ── 用四个问题决定
把到目前为止的内容收成选择程序。按序回答四个问题。
flowchart TB
accTitle: 登录帐户的判断流程
accDescr: 按序回答是否有网络访问、是否加入域、机器级身份是否足够、是否支持 gMSA 这四个问题,决定登录帐户
q1{"对端要 Windows 验证?"} -->|否| va["虚拟帐户"]
va -.-> sys["需要时用 LocalSystem"]
q1 -->|是| q2{"已加入域?"}
q2 -->|否| cred["保护存储的凭据"]
q2 -->|是| q3{"机器级就够?"}
q3 -->|是| pcacl["虚拟帐户 + PC$"]
q3 -->|否| q4{"应用程序支持 gMSA?"}
q4 -->|是| gmsa["gMSA"]
q4 -->|否| du["用户 + 缓解"]
图 22: 按序回答四个问题,六种选择该用哪一种就定了。
问题 1:该服务是否以 Windows 身份验证访问网络上的另一台机器(共享文件夹、数据库、API 等)?
若否,虚拟帐户是默认。只有需要特殊本地权限时,先确认需求再考虑 LocalSystem。
问题 2:(若有访问)机器是否已加入域?
工作组既不能用 PC$ 也不能用 gMSA。使用显式处理目标端帐户凭据的设计(用 DPAPI 等保护存储),或考虑加入域。
问题 3:(在域内)机器级身份(PC$)够用吗?
够用就 虚拟帐户(或 NetworkService)+ 在目标端 ACL 授予 PC$ 即完成。若需要服务专属身份,或跨多台服务器的共同身份,进问题 4。
问题 4:应用程序支持 gMSA 吗?
支持(通过标准机制设置登录的 ── SCM、IIS 应用程序池、任务计划程序 ── 一般支持)就用 gMSA。别忘了在验证环境做行为检查。再怎样都不支持,就在套用 7.3 节全部缓解后使用 专用域用户。
做成表如下。
| 状况 | 建议 | 备注 |
|---|---|---|
| 仅本地、一般权限 | 虚拟帐户 | 把 ACL 授予 NT SERVICE\<名称> |
| 仅本地、需要超出管理员的权限 | LocalSystem | 先验证权限需求 |
| 不需要网络身份的本地处理 | LocalService | 维持既有服务现状时可接受 |
| 以机器身份访问域内资源 | 虚拟帐户(或 NetworkService) | 在目标端 ACL 授予 PC$ |
| 以服务专属身份访问域内资源 | gMSA | KDS 根密钥 + 确认支持 |
| 多台服务器同一身份(负载均衡等) | gMSA | 虚拟帐户做不到 |
| 不支持 gMSA 的应用程序 + 需要特定身份 | 专用域用户 | 需要 7.3 节的缓解 |
| 工作组 + 需要远程访问 | 保护并存储显式凭据 | 也考虑重新审视设计 |
11. 总结
- 服务的登录帐户是同时决定本地权限、网络身份与密码管理的设计决策。不要留在默认(LocalSystem)。
- LocalSystem 持有 SYSTEM+Administrators 令牌与强特权,被接管时伤害最大化。多数业务服务不需要这个权限。
- LocalService 与 NetworkService 都是低权限;差别是网络身份(匿名,或计算机帐户)。但帐户由多个服务共用,因此分不开。
- 虚拟帐户(NT SERVICE\<服务名称>)是现代默认:不必管密码又能按服务分开。可直接指定到 ACL,配置只需改登录帐户名。服务名称>
- LocalSystem、NetworkService、虚拟帐户在域环境走上网络时是 DOMAIN\PC$。在共享文件夹或 SQL Server 的 ACL 授予 PC$,常常就能不用域用户。
- 把域用户用在服务上,有到期停摆、明文密码扩散、Kerberoasting 这些结构性问题。若要用,需要专用帐户 + 长随机密码 + 登录限制。
- gMSA 是 AD 自动生成并轮换密码的机制;要件是域、功能级别 2012 或更高、KDS 根密钥。服务设成“DOMAIN\名称$”且密码栏留空。
- 改帐户时,把“作为服务登录”权利、配置文件与 %TEMP% 的搬移、DPAPI 保护数据的重新输入写进迁移程序。审计可用事件 ID 4624 登录类型 5 确认。
下次安装服务时,在登录设置画面停一下,再问一次。这个服务应该以谁的身份、访问到多远? 答案应该是本文判断表的某一行。
相关文章
- Windows 服务的创建与运维 ── 从任务计划程序的取舍到 BackgroundService 服务化
- Windows 什么时候需要管理员权限 - UAC、保护区域与设计上的辨别方式
- 正确处理 Windows 的模拟令牌 ── 线程级权限借用与安全的还原方式
- 图解 NTLM 与 Kerberos ── 为什么认证会「回退」到 NTLM
- Windows LAPS实务指南 ── 告别全部PC通用的本地管理员密码
- Windows 应用程序不要把敏感信息以明文存进配置文件的最佳实践
相关咨询领域
小村软件有限公司承接 Windows 服务与常驻应用程序的登录帐户设计与最小权限强化、把以 LocalSystem 为前提打造的既有服务迁到虚拟帐户或 gMSA,以及帐户变更后因访问被拒、DPAPI、配置文件引起的故障调查。从“审计被点名了,但不知道从哪里开始”这个阶段开始也可以。
参考链接
-
Microsoft Learn, Configure Windows service accounts and permissions. SQL Server 的默认服务帐户是虚拟帐户(NT SERVICE\MSSQLSERVER 等)、指定虚拟帐户或 MSA 时密码栏留空、MSA 名称结尾为 $ 且不能用于交互式登录、Local Service 是共用帐户故分不开且 SQL Server 不支持、使用域帐户在密码与 SPN 的手动管理上耗费力气且维护可能导致服务停止,以及应一律以最小权限帐户运行服务。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Securing on-premises service accounts. 本地部署服务的优先顺序是先 gMSA、不能用再用 sMSA、然后计算机帐户、最后用户帐户;使用计算机帐户时无法得知哪个服务在用该帐户也不能审计更改;以及服务帐户的角色(识别、验证并启动服务)。 ↩ ↩2 ↩3
-
Microsoft Learn, LocalSystem Account. LocalSystem 在本地计算机持有广泛权限且令牌含 NT AUTHORITY\SYSTEM 与 BUILTIN\Administrators 的 SID、没有密码、向远程服务器出示计算机的凭据、含 SE_DEBUG_NAME 与 SE_TCB_NAME 的特权清单,以及多数服务不需要这个权限级别、应考虑使用 LocalService/NetworkService。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, sc.exe config. 用 obj= 参数指定服务的登录帐户、默认是 LocalSystem,以及使用 LocalSystem 以外的用户帐户时的 password= 参数。 ↩ ↩2
-
Microsoft Learn, Local accounts. SYSTEM(S-1-5-18)在 NTFS 卷默认具有完全控制、NETWORK SERVICE(S-1-5-20)向远程服务器出示计算机的凭据,以及 LOCAL SERVICE(S-1-5-19)本地权限最小、向网络出示匿名凭据。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Service accounts. 虚拟帐户是不需密码管理的自动管理本地帐户、名称为 NT SERVICE<SERVICENAME> 形式、在域环境以计算机帐户凭据(
\ ↩ ↩2 ↩3 ↩4$)访问网络,以及在 sMSA、gMSA、dMSA、虚拟帐户之间选择的准则。 -
Microsoft Learn, Service User Accounts. 服务在用户帐户的安全上下文中运行、SCM 在启动时登录帐户并把访问令牌关联到服务进程、SCM 加载用户配置文件,以及 SCM 不管密码到期,到期会让登录失败、服务无法启动。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Protect SMB traffic from interception. 包含以 gMSA 作为服务帐户保护(机器生成的长随机密码使暴力破解或字典攻击不实际)、强制长密码,以及提及 Kerberos 装甲(FAST)等建议。 ↩ ↩2
-
Microsoft Learn, Secure group managed service accounts. gMSA 密码是难以暴力破解或字典攻击的 240 字节随机生成、Windows 操作系统每 30 天更改密码故管理员不必规划更改或停止服务、部署到服务器场与更单纯的 SPN 管理、若服务不支持 gMSA 就用 sMSA、再不行就用有强密码管理的标准用户帐户,以及正式上线前应在测试环境确认以 gMSA 运行的行为。 ↩ ↩2 ↩3
-
Microsoft Learn, Manage group Managed Service Accounts. gMSA 前提(域/林功能级别 2012 或更高、创建 KDS 根密钥)、gMSA 名称必须在林内唯一、密码更改间隔只能在创建时设置、用 New-ADServiceAccount 的 -PrincipalsAllowedToRetrieveManagedPassword 指定获准取回密码的组、Install-ADServiceAccount/Test-ADServiceAccount 程序、虚拟帐户身份是机器本地且域认不出、故障转移群集不支持 gMSA,以及 SCM、IIS 应用程序池、任务计划程序支持把登录设成 gMSA。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, 4624(S): An account was successfully logged on. 创建登录会话时事件 4624 记录在被访问的计算机上、登录类型 5 表示服务(SCM 启动服务),以及“Virtual Account”字段可识别 MSA 或虚拟帐户的登录,可用来监视托管服务帐户。 ↩ ↩2
-
Microsoft Learn, About Windows Resource Protection. Windows 资源保护(WRP)防止重要系统文件、文件夹与注册表项被替换、对 WRP 保护资源的完全访问限于 TrustedInstaller 且更改只能通过 Windows Modules Installer 服务的受支持替换机制进行,以及试图更改受保护资源的应用程序会收到访问被拒。 ↩
-
Microsoft Learn, Group Managed Service Accounts overview. gMSA 是把密码管理交给 Windows 的域帐户、域控制器从 Key Distribution Service(kdssvc.dll)共享机密计算密码且成员主机向域控制器查询当前与先前密码,以及它让服务器场能以同一主体互相验证。 ↩
-
Microsoft Learn, Create a Key Distribution Service (KDS) root key. 域控制器开始生成 gMSA 密码需要根密钥、用 Add-KdsRootKey -EffectiveImmediately 创建的程序、创建后最多 10 小时因等待 AD 复制收敛而不能创建 gMSA,以及复制不完整可能让密码取回失败。 ↩
-
Microsoft Learn, Policy CSP - UserRights: LogOnAsService. “作为服务登录”权利让安全主体能以服务登录、Local System、Local Service、Network Service 内置此权利、以其他帐户运行的服务需要指派此权利,以及组策略配置路径。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
Windows LAPS实务指南 ── 告别全部PC通用的本地管理员密码
全部PC通用的本地管理员密码,是「一台被攻破、全部沦陷」的Pass-the-Hash攻击温床。本文讲解已成为OS标准功能的Windows LAPS如何自动轮换密码、如何配置保存到AD/Entra ID,以及运维中的常见陷阱。
SMB 签名与 LDAP 通道绑定 ── 用实务收紧 NTLM 对策的「另一半」
在停用 NTLM 之前,用来抑制中继攻击损害的防御手段就是 SMB 签名与 LDAP 签名・通道绑定。本文从实务角度整理各操作系统的默认值、审核事件的解读方法、推进到强制的步骤,直至业务应用与设备的修复方法。
图解 NTLM 与 Kerberos ── 为什么认证会「回退」到 NTLM
本文通过图解整理 NTLM 与 Kerberos 的区别,涵盖挑战/响应机制、TGT 与服务票据、SPN 无法解析时 Negotiate 回退到 NTLM 的条件、中继攻击与 Pass-the-Hash 得以成立的原因,直至 NTLMv1 被移除,并附有官方文档依据。
NTLM 停用会导致业务应用停止运行吗 ── 审核日志的获取方法与消除依赖的顺序
围绕 NTLM 停用,本文整理了排查自身 Windows 环境与业务应用在何处依赖 NTLM 的步骤。内容涵盖审核策略、NTLM/Operational 日志中事件 8001~8004 的追踪方法、回退到 NTLM 的典型模式及修复方式,直至 SMB 的 NTLM 拦截功能。
从应用看到的 Windows 关机 ── 正确扛住退出通知、重启与断电
夜间 Windows Update 重启把测量数据弄坏——这种事故可用设计避免。本文依一次信息整理 WM_QUERYENDSESSION 与 PRESHUTDOWN 等结束通知的接法、几秒内做完收尾,以及断电也不留下半截文件的写法。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 目前以 LocalSystem 运行的服务,应该立刻改掉吗?
- 并非一律立刻变更才正确。先确认该服务是否真的需要 LocalSystem 级的本地权限(超出管理员的强特权)。若只是文件读写与网络通信,第一候选是改到虚拟帐户(NT SERVICE\服务名称)。迁移时请确认对所需文件夹与注册表项的访问授予、依赖配置文件或 DPAPI 的数据如何处理,以及是否具备“作为服务登录”权利。在验证环境确认启动与主要功能后,再切换生产环境。
- 应该选虚拟帐户还是 NetworkService?
- 新选时建议虚拟帐户。两者在网络上都显示为计算机帐户(DOMAIN\计算机名$),本地权限也都偏小。但 NetworkService 由多个服务共用,无法用 ACL 做到“只允许这个服务”。虚拟帐户每个服务都有独立身份,可在 ACL 上直接指定 NT SERVICE\服务名称。SQL Server 等近年 Microsoft 产品的默认也是虚拟帐户。
- gMSA 能在工作组环境(没有域)使用吗?
- 不能。gMSA 是由 Active Directory 域控制器生成并管理密码的机制,前提是域与创建 KDS 根密钥。工作组环境的基本做法是用虚拟帐户或 LocalService/NetworkService 把本地处理做完。若需要访问另一台机器,就要改成显式使用目标端准备好的帐户凭据等其他设计。以计算机帐户(PC$)进行的网络访问也只在域环境成立。
- 改了服务的登录帐户后,先前保存的设置与凭据就读不到了。为什么?
- 因为每个登录帐户都绑定自己的用户配置文件、%TEMP%、HKEY_CURRENT_USER 与 DPAPI 密钥。尤其以用户范围 DPAPI(CryptProtectData 等)保护的数据,原则上只有当初保护它的同一个帐户才能解密。存在配置文件底下(AppData 等)的文件,对新帐户也是另一条路径。切换帐户前,请规划重新创建 DPAPI 保护数据的程序(例如重新输入 API 密钥)以及配置文件底下文件的迁移。
- 只想让服务访问共享文件夹,需要域用户吗?
- 多数情况不需要。在域环境中,以 LocalSystem、NetworkService 或虚拟帐户运行的服务,会以计算机帐户(DOMAIN\计算机名$)向远程验证。把该 PC$ 加到共享权限与 NTFS 权限即可读写。若要以服务专属身份做访问控制,或在多台服务器上需要同一身份,请考虑 gMSA 而不是域用户。