基于前文 ChatGPT 提到的如下理论,我实际测试了一下 AD 中的命名规则。首先,CNF、DEL 本身并不是 AD 的保留字或特殊字符,只有当它们和 \0A(换行符) 一起使用时才有特殊含义。而根据实践,在这些 User 对象里面真正被限制的其实是字符 \ 和 :,至于普通对象则并没有此类限制。
Active Directory 不阻止用户人为创建包含
\0ACNF:或\0ADEL:的 CN / DN
User Object
限制创建 DEL/CNF User Object 名称的特殊字符
可以看出 : 和 \ 是被严格限制的字符,不能直接用于 CN 中。
限制创建 User Object 名称的其他特殊字符
除了 : 和 \ , 我还测试了一些其他特殊字符,受限制的字符还包括 *、=、+、[、]、;、"、,、<、.、>、/、? 和 |。而 !、@、#、$、%、^、&、(、)、-、_、{、}、'、`、~ 等都可以正常作为名字的一部分输入。(可以联想到 RID Master 的 CN 为 RID Manager$,侧面也说明 $ 是可以被使用的。)
后来确认,其实是属性 sAMAccountName 在限制这些字符。官方文档 有明确列出不能使用的字符 🤦🏻。不过好像文档里并没有提到点号 .,问了下 ChatGPT (是的我还爱它 ❤️),是被 UPN 限制,只是不能加在末尾,实际测试也是这样。
🧠 为什么实际使用中有时感觉 “
.不行”?这是因为
.@通常出现在 UPN(UserPrincipalName) 中,如:CN=Charles Feng, sAMAccountName=charles.feng, userPrincipalName=charles.feng@contoso.com
- 若你在
sAMAccountName和userPrincipalName都用.,一些系统或脚本可能混淆- Windows 登录时,如果输入
charles.feng,系统可能误以为是本地账户而不是域账户- 某些 LDAP query 或 PowerShell 脚本解析上可能会误解
.是别的分隔符(尤其在正则、路径等上下文中)✅ 实际建议
- 允许使用
., 但建议置于中间而非结尾(如charles.feng可以,charles.可能导致解析问题)- 不要让多个用户
sAMAccountName只靠.差异化(如john,john.smith,否则易混)- 如果你在使用某个系统或工具时遇到点号报错,可以明确查是 UI、脚本、登录行为还是 legacy 系统做了限制
User Objects 实际存储在 ESE 数据库中的数据
需要注意的是,对 User ChewDavidCNF1234# (DNT 10239),其在数据库中实际存储的属性 name 并不包含 \。但通过 LDP 查看时会显示 \,这可能是由于转义或显示格式所致。
User Object 名称长度的限制
另外,我发现 New-ADUser 对名字有长度不能超过 20 个字符的限制,但是理论上对一个普通的 AD object 不会有这样的限制,因为我也看到过很多 object 的名字是超过 20 个字符的。
查看了类 User 上跟 name 相关的属性,发现虽然 sAMAccountName 的 Range-Upper 指定的是 256 个字符,但是有特别说明不能超过 20 个字符,所以限制正是源自这里。当然,根本原因应该更底层,因为这个属性是为了支持 Windows 98 之前的系统了 🥱。不过这种坑,除非踩到,不然谁知道啊!AD 这方面真的蛮难用的。。(顺带一提,对我组的 Sync Engine 来说,这个属性也是不需要维护的,说明在 M365 内部它已经被彻底 deprecated 掉了。)
SAM (Security Accounts Manager) 是 Windows 系统的本地账号数据库。它存在于每台独立的 Windows 电脑上(路径通常为
C:\Windows\System32\config\SAM),负责管理这台电脑本地的用户名和密码。AD 则是把类似的账号与权限模型扩展成了整个域的分布式目录服务。
- 演进与替代:在没有 AD 的早期网络(如 Windows NT 4.0)中,企业依赖 SAM 来进行网络认证。由于 SAM 无法支撑大规模网络,微软在 Windows 2000 中推出了 AD。在企业域环境中,AD 替代了本地 SAM 的核心地位。
- 属性继承 (
sAMAccountName):为了兼容旧系统,AD 在设计时继承了 SAM 的命名规范。
- AD 中每个用户对象的登录名属性就被命名为
sAMAccountName(即俗称的旧版登录名,格式为DOMAIN\username)。- 别的
sAM*属性不一定是为了兼容本地 SAM 数据库,比如sAMAccountType把 AD 安全主体表达为 SAM/SAMR 账户类型,同时仍用于实际查询和枚举;samDomainUpdates用于 AD 内部跟踪 SAM 域模型的默认账户、组和成员关系升级。- 共存机制:
- 普通电脑或成员服务器:SAM 和 AD 同时存在。登录时,Windows 根据账号类型决定去哪里验证,加入域不会删除本机 SAM。
- 本地账号保存在本地 SAM 数据库中,例如:
MYPC\alice- 域账号保存在 Domain Controller 的 AD 中,例如:
CONTOSO\alice- 域控制器上:AD 取代普通本地 SAM。域控制器的域用户、域组和计算机账号保存在 AD 数据库
C:\Windows\NTDS\ntds.dit中。
- 不再拥有普通意义上的本地用户和本地组
DOMAIN\Administrator是 AD 中的域账号- 账号、组、密码摘要等由 AD DS 管理并在 DC 之间复制
- 一个特殊例外是 DSRM(目录服务还原模式)管理员密码,它仍与本机 SAM 机制有关,供 AD 无法启动时进行恢复。
- SID 相关
- SID(Security Identifier,并不是 SAM ID) 是 Windows 用来唯一识别安全主体(用户、组、计算机账户、服务账户、某些内置身份,如
SYSTEM、Everyone等)的 ID,被 Windows 真正用于权限判断,而不是用户名。(这样即使用户重命名,SID 不变,原有权限仍然有效。)- SAM 和 AD 都负责保存账户及其 SID 到对应数据库,ACL 和访问令牌则使用 SID 完成权限判断。
FYI,其他属性如 cn/Common-Name、sn/Surname、 givenName、middleName/Other-Name 限制为 64 个字符,displayName、adminDisplayName 等限制为 256 个字符。不过,真正 mandatory 的 name 相关的属性只有 cn 和 sAMAccountName 啦。
普通 AD Object
对普通 Object 的命名限制
不过,好像这整个测试本来是为了验证是不是能主动创建一个类似 DEL/CNF 命名规则的 AD object……让我们回归一下初心 👋,跳出 User object 的限制,看看普通 object。
使用命令 New-ADObject 创建 Container object 时可以发现,之前在 User object 上被限制的字符,在这里都可以正常作为名称的一部分被使用,包括结尾带点号 . 也是没问题的。
上图还可以看出,我们确实可以成功创建符合 DEL/CNF 命名规则的对象。通过 ldp 查看这些对象时,类似地,我们可以看到部分字符被转义,包括 \、"、,、;、+、<、=、> 等。
普通 Objects 实际存储在 ESE 数据库中的数据
但是当我们查看数据库数据时,可以发现:
- ldp 中转义的字符是原文存储在数据库
name中的,转义是 ldp 的行为。 - 手动输入的
\0A不会被识别为换行符。以 DNT 为 10276/10277 的对象为例,它们的name并不会像 DNT 为 10232/10233/10253 的那样在显示时真的换行。
通过 New-ADObject 创建其他类型的 Objects
来都来了,看这个命令的 examples 里还有类型 Contact 和 Subnet,也就都试一试。
总结
- ChatGPT 当时说得基本没问题:对普通 AD object 而言,AD 并不阻止用户人为创建包含
\0ACNF:或\0ADEL:的 CN / DN。 - 如果非要输入 DEL/CNF 格式的名称,从数据库的角度看,AD 是能够通过区分
\0A来判断一个对象是不是真正的系统产生的 DEL/CNF 特殊对象。 - AD User object 会因为 legacy 的属性
sAMAccountName被限制名称不能超过 20 个字符。
