PowerShell文件系统(五)管理访问权限 3


PowerShell 文件系统系列文章:

对于NTFS驱动器来说,访问权限决定着那个用户可以访问文件和目录。对于每一个文件和文件夹,所谓的安全描述符(SD)规定了安全数据。安全描述符决定安全设置是否只对当前目录有效,或者它可以被传递给其它文件和目录。真正的访问权限是在访问控制列表(ACL)中。每一个访问权限的访问控制项(ACE)也在ACL中。

注意:文件和目录访问权限相当于一个复杂的电子锁。如果使用得当,你可以把它变成一个有力的安全系统。然而,如果使用不当,你可能很容易把自己锁在外面,失去了访问重要数据的权限,或者破坏了Windows操作系统(当你无意间禁止了访问关键系统目录的权限后)。作为文件和目录的所有者,你总是有更正权限的选项;作为一个管理员,你也总能取得文件和目录的拥有权。但这是不得已的后门,你不能依赖它:你应当在你能意识到后果的情况下更改权限。最好一开始使用测试文件和目录做实验。

PowerShell使用Get-Acl 命令 Set-Acl 来管理权限。此外,类似cacls这样的传统命令也可以在PowerShell的控制台上面使用。通常他们更改起来访问权限会比PowerShell命令更快。尤其在你处理非常多的文件和目录时。由于Windows Vista的发布,cacls一直被视为过时。如果可能的化,你可以使用它的继任者icacls

PS> icacls /?

ICACLS name /save aclfile [/T] [/C] [/L] [/Q]
    将匹配名称的文件和文件夹的 DACL 存储到 aclfile 中
    以便将来与 /restore 一起使用。请注意,未保存 SACL、
    所有者或完整性标签。

ICACLS directory [/substitute SidOld SidNew [...]] /restore aclfile
                 [/C] [/L] [/Q]
    将存储的 DACL 应用于目录中的文件。

ICACLS name /setowner user [/T] [/C] [/L] [/Q]
    更改所有匹配名称的所有者。该选项不会强制更改所有
    身份;使用 takeown.exe 实用程序可实现
    该目的。

ICACLS name /findsid Sid [/T] [/C] [/L] [/Q]
    查找包含显式提及 SID 的 ACL 的
    所有匹配名称。

ICACLS name /verify [/T] [/C] [/L] [/Q]
    查找其 ACL 不规范或长度与 ACE
    计数不一致的所有文件。

ICACLS name /reset [/T] [/C] [/L] [/Q]
    为所有匹配文件使用默认继承的 ACL 替换 ACL。

ICACLS name [/grant[:r] Sid:perm[...]]
       [/deny Sid:perm [...]]
       [/remove[:g|:d]] Sid[...]] [/T] [/C] [/L] [/Q]
       [/setintegritylevel Level:policy[...]]

    /grant[:r] Sid:perm 授予指定的用户访问权限。如果使用 :r,
        这些权限将替换以前授予的所有显式权限。
        如果不使用 :r,这些权限将添加到以前授予的
        所有显式权限。

    /deny Sid:perm 显式拒绝指定的用户访问权限。
        将为列出的权限添加显式拒绝 ACE,
        并删除所有显式授予的权限中的相同权限。

    /remove[:[g|d]] Sid 删除 ACL 中所有出现的 SID。使用
        :g,将删除授予该 SID 的所有权限。使用
        :d,将删除拒绝该 SID 的所有权限。

    /setintegritylevel [(CI)(OI)]级别将完整性 ACE 显式
        添加到所有匹配文件。要指定的级别为以下级别
        之一:
             L[ow]
             M[edium]
             H[igh]
        完整性 ACE 的继承选项可以优先于级别,但只应用于
        目录。

    /inheritance:e|d|r
        e - 启用继承
        d - 禁用继承并复制 ACE
        r - 删除所有继承的 ACE

注意:
    Sid 可以采用数字格式或友好的名称格式。如果给定数字格式,
    那么请在 SID 的开头添加一个 *。

    /T 指示在以该名称指定的目录下的所有匹配文件/目录上
        执行此操作。

    /C 指示此操作将在所有文件错误上继续进行。
        仍将显示错误消息。

    /L 指示此操作在符号
      链接本身而不是其目标上执行。

    /Q 指示 icacls 应该禁止显示成功消息。

    ICACLS 保留 ACE 项的规范顺序:
            显式拒绝
            显式授予
            继承的拒绝
            继承的授予

    perm 是权限掩码,可以指定两种格式之一:
        简单权限序列:
                N - 无访问权限
                F - 完全访问权限
                M - 修改权限
                RX - 读取和执行权限
                R - 只读权限
                W - 只写权限
                D - 删除权限
        在括号中以逗号分隔的特定权限列表:
                DE - 删除
                RC - 读取控制
                WDAC - 写入 DAC
                WO - 写入所有者
                S - 同步
                AS - 访问系统安全性
                MA - 允许的最大值
                GR - 一般性读取
                GW - 一般性写入
                GE - 一般性执行
                GA - 全为一般性
                RD - 读取数据/列出目录
                WD - 写入数据/添加文件
                AD - 附加数据/添加子目录
                REA - 读取扩展属性
                WEA - 写入扩展属性
                X - 执行/遍历
                DC - 删除子项
                RA - 读取属性
                WA - 写入属性
        继承权限可以优先于每种格式,但只应用于
        目录:
                (OI) - 对象继承
                (CI) - 容器继承
                (IO) - 仅继承
                (NP) - 不传播继承
                (I) - 从父容器继承的权限

示例:

        icacls c:\windows\* /save AclFile /T
        - 将 c:\windows 及其子目录下所有文件的
          ACL 保存到 AclFile。

        icacls c:\windows\ /restore AclFile
        - 将还原 c:\windows 及其子目录下存在的 AclFile 内
          所有文件的 ACL。

        icacls file /grant Administrator:(D,WDAC)
        - 将授予用户对文件删除和写入 DAC 的管理员
          权限。

        icacls file /grant *S-1-1-0:(D,WDAC)
        - 将授予由 sid S-1-1-0 定义的用户对文件删除和
          写入 DAC 的权限。

检查有效的安全设置

文件和目录的有效安全设置在访问控制列表中,使用Get-Acl时,会获取列表中的内容。因此如果你想找出谁能够访问某些文件或者目录,可以这样处理:

# 列出Windows目录的权限:
PS> Get-Acl $env:windir

    Directory: C:\

Path    Owner                       Access
----    -----                       ------
Windows NT SERVICE\TrustedInstaller CREATOR OWNER Allow  268435456...

确认文件所有者的身份

文件和目录的所有者还有一些特殊的权限。比如文件的所有者总是能够访问文件。你可以通过Owner属性,来获取所有者名称。

(Get-Acl $env:windir).Owner

    NT SERVICE\TrustedInstaller

列出访问权限

实际上访问权限就是——谁可以做什么,下面输出访问属性:

PS> (Get-Acl $env:windir).Access | Format-Table -wrap

   FileSystemRights   AccessControlType IdentityReference           IsInherited    InheritanceFlags    PropagationFlags
   ----------------   ----------------- -----------------           -----------    ----------------    ----------------
          268435456               Allow CREATOR OWNER                     False  ContainerInherit,          InheritOnly
                                                                                      ObjectInherit
          268435456               Allow NT AUTHORITY\SYSTEM               False  ContainerInherit,          InheritOnly
                                                                                      ObjectInherit
Modify, Synchronize               Allow NT AUTHORITY\SYSTEM               False                None                None
          268435456               Allow BUILTIN\Administrat               False  ContainerInherit,          InheritOnly
                                        ors                                           ObjectInherit
Modify, Synchronize               Allow BUILTIN\Administrat               False                None                None
                                        ors
        -1610612736               Allow BUILTIN\Users                     False  ContainerInherit,          InheritOnly
                                                                                      ObjectInherit
   ReadAndExecute,                Allow BUILTIN\Users                     False                None                None
        Synchronize
          268435456               Allow NT SERVICE\TrustedI               False    ContainerInherit         InheritOnly
                                        nstaller
        FullControl               Allow NT SERVICE\TrustedI               False                None                None
                                        nstaller

在上面表格的IdentityReference列,告诉你谁有特殊的权限。FileSystemRights列告诉你权限的类型。AccessControlType列格外重要,如果它显示“拒绝”而不是“允许”,你懂的,它会限制用户访问。

创建新的权限

Get-Acl执行后返回的对象,包含若干方法可以用来更新权限和设定所有权。如果你只想设定自己的权限,都没必要去安全描述符世界深究。往往,读取一个已经存在的文件安全描述符,把它传递给另一个文件,或者按照特殊SDDL语言文字的形式指定安全信息就够了。

技巧:下面的例子会让你认识一些日常步骤。注意两点即可:别忘了cacls这个可靠的工具,因为使用它会比PowerShell命令更高效。此外,Get-ACL和Set-ACL不仅仅应用于文件层面,还可以用于其它有访问控制的安全描述符的任何地方,比如Windows注册表(会在下一章讲解)。

克隆权限

在一个初级的案例中,你可能都不会创建任何新的权限,只会从一个已经存在的文件或者目录的访问控制列表中克隆一个权限,然后把它转让给其它文件。优点是可以使用图形用户界面来设置那些通常比较复杂的权限。

注意:因为手动调整安全设置是一项专业,各个Windows系统不通用(像Windows XP Home就没有这个选项)的工作。尽管如此,你却可以使用PowerShell在不同的Windows版本中设置权限。

开始之前,先创建两个目录作为测试:

md Prototype | out-null
md Protected | out-null

现在,打开资源管理器,设置Prototype目录的安全设置。

explorer .

在资源管理器中,右击Prototype目录,选择属性,然后点击安全选项卡,点击编辑(win7和win8中)。通过添加其他用户来更改测试目录的安全设置。在下面的对话框中给新用户设置权限。

注意:你也可以通过勾选拒绝复选框来拒绝用户的权限。这样做时,可要留心了。因为限制权限总是有高优先级。比如,你给了自己完全控制的权限,但是拒绝了“Everyone”这个组来访问。这样就把自己关在文件系统的外面了。因为你也属于”Everyone”这个组,同时因为限制的优先级比较高,哪怕你已经给了自己“完全控制”的权限,这个限制也作用于你。

你更改了权限后,捎带在资源管理器中看看第二个目录Protected。这个目录仍旧是默认赋予的权限。下一步,我们会把Prototype刚才设置的权限转交到Protected目录。

$acl = Get-Acl Prototype
Set-Acl Protected $acl

注意:你本身需要特殊的权限去设置上面的权限。如果你用的是Windows Vista操作系统,并且启用了UAC,使用PowerShell操作时,会出现错误,提示你没有权限。这时可以通过让控制台以管理员权限运行来获取权限。

实验做完了,现在呢,Protected和Prototype一样安全。当你在资源管理器中查看它们的安全设置时,你会发现所有的设置都是相同的。

使用SDDL设置权限

前面的例子非常简单,你所做的只是把已有目录的安全设置移交给其它目录。在你的日常工作中,你可能得具备一个你根本就不需要的Prototype目录。但是你可以通过文本格式的安全描述符来归纳安全设置。每一个安全设置都是被特殊的安全描述符描述语言(SDDL)定义的。它能让你以文本的形式读取Prototype目录的安全信息,以后无须借助Prototype目录即可使用。

让我们删掉这个测试目录Protected吧,然后在SDDL中保存Prototype目录的安全信息。

PS> Del Protected
PS> $acl = Get-Acl Prototype
PS> $sddl = $acl.Sddl
PS> $sddl
O:S-1-5-21-2146773085-903363285-719344707-1282827G:DUD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;S-1-5-21-2146773085-903363285-719344707-1282827)(A;OICI;FR;;;S-1-5-21-2575865618-2571387221-2201921913-1000)

然后把这个SDDL文本保存到第二个脚本中,可将该安全设置赋给任意目录。

# 创建新目录
Md Protected
# 在 SDDL中的是安全描述符 (一行):
$sddl = "O:S-1-5-21-2146773085-903363285-719344707-1282827G:DUD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;S-1-5-21-2146773085-903363285-719344707-1282827)(A;OICI;FR;;;S-1-5-21-2575865618-2571387221-2201921913-1000)"
# 获取目录的安全描述:
$acl = Get-Acl Protected
# 使用SDDL定义替换安全描述 :
$acl.SetSecurityDescriptorSddlForm($sddl)
# 保存更新
Set-Acl Protected $acl

注意:你的第二个目录是完全独立于Prototype目录的。你所需要做的可能是,借助Prototype目录使用图形用户界面,临时生成一个SDDL安全设置定义。

然而,SDDL不能很方便的移交给其它机器。如果你仔细看下,每个授权用户不是根据用户名识别,而是根据它们的安全标识符(SID)识别。不同的机器上,即使用户名相同,这个SID也不会相同,因为它们隶属不同的账户。但是在一个域(domain)中,相同名字的账号的SID是相同的,因为域会集中管理。其结果就是SDDL解决方案在基于域环境的公司网络中非常完美。尽管如此,如果你处在一个小型的对等网络中,SDDL也能非常有用。你只需要使用“复制黏贴”去替换SID而已。不过,在对等网络中,cacls 或者 icacls命令可能更简单一点。

手动创建新权限

权限也可以被手动创建。其优点就是,即使没有集中域,你也可以根据用户名来指定授权用户,这样可以以相同的方式在任意机器上工作。但是注意,它引入了额外的工作,因为你必须完全创建你自己的安全描述符,接下来的例子会展示。但是在实践中发现这个过程非常的耗时。使用cacls和icacls都比它简单一点。现在我们删除掉测试目录Protected,再次创建一个新的目录,让它只有默认的访问权限。

$acl = Get-Acl Protected
# 添加第一个规则:
$person = [System.Security.Principal.NTAccount]"Administrator"
$access = [System.Security.AccessControl.FileSystemRights]"FullControl"
$inheritance = [System.Security.AccessControl.InheritanceFlags] "ObjectInherit,ContainerInherit"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( $person,$access,$inheritance,$propagation,$type)
$acl.AddAccessRule($rule)

# 添加第二个规则:
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.FileSystemRights]"ReadAndExecute"
$inheritance = [System.Security.AccessControl.InheritanceFlags] "ObjectInherit,ContainerInherit"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( $person,$access,$inheritance,$propagation,$type)
$acl.AddAccessRule($rule)

# 保存权限更新:
Set-Acl Protected $acl

接下来,让我们一起看看每个访问规则是怎么定义的。每一个规则需要5个细节:

  • Person:这是该规则应当适用的人或者组。
  • Access:这里选择规则要控制的权限。
  • Inheritance:这里选择规则要应用的对象。这个规则能够,并且一般是会授予它的子对象,这样它就能自动适用于目录中的文件了。
  • Propagation:决定权限是否要传递给子对象(比如子目录和文件),通常情况下设置为None,仅仅授予权限。
  • Type:它能让你设置权限或者限制,如果限制,指定的权限会明确不予批准。

接下来问题是这些规范允许那些值?这个例子演示通过.NET对象(第六章)显示这些规范。你可以使用下面的机器列出访问权限允许的值:

[System.Enum]::GetNames([System.Security.AccessControl.FileSystemRights])
ListDirectory
ReadData
WriteData
CreateFiles
CreateDirectories
AppendData
ReadExtendedAttributes
WriteExtendedAttributes
Traverse
ExecuteFile
DeleteSubdirectoriesAndFiles
ReadAttributes
WriteAttributes
Write
Delete
ReadPermissions
Read
ReadAndExecute
Modify
ChangePermissions
TakeOwnership
Synchronize
FullControl

如果你想设置权限时,实际上得结合上面列表中列出的相关值,比如:

$access = [System.Security.AccessControl.FileSystemRights]::Read `
-bor [System.Security.AccessControl.FileSystemRights]::Write
$access

131209

结果是一个数字,读和写权限的位掩码。在上面的例子中,你可以非常简单第获取相同的结果,因为允许你指定你想要的项目,甚至把它们放在一个逗号分隔项中,紧跟在括号括起来的.NET枚举类型后面。

$access = [System.Security.AccessControl.FileSystemRights]"Read,Write"
$access
   Write, Read

[int]$access
   131209

因为这里你没有指定二进制计算符-bor,它的结果是可读的文本。而此时需要位掩码来工作,所以把它转换成Integer整形数据类型。你可以像这样随时得出设置的相关值。

[int][System.Security.AccessControl.InheritanceFlags] `
"ObjectInherit,ContainerInherit"
3

这样做的意义在于你现在可以测试其它.NET枚举类型的值,把它们转换成整数。虽然不能增强你的命令的可读性,但是可以压缩脚本。因为下面的脚本行和前面例子中的脚本行可以做同一件事。

Del Protected
Md Protected
$acl = Get-Acl Protected
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( "Administrator",2032127,3,0,0)
$acl.AddAccessRule($rule)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( "Everyone",131241,3,0,0)
$acl.AddAccessRule($rule)
# 保存更新的权限:
Set-Acl Protected $acl

最后,我们看看PowerShell是怎么指定特定用户的权限的。在上面的例子中,你指定了用户或者组的名称,但是权限不能识别用户名,但能识别账号的唯一SID,用户名在内部会被更改成SID,你也可以在脚本中手动更改用户名,看看指定的用户名是否存在。

$Account = [System.Security.Principal.NTAccount]"Administrators"
$SID = $Account.translate([System.Security.Principal.Securityidentifier])
$SID
BinaryLength	AccountDomainSid	Value
------------	----------------	-----
16 S-1-5-32-544

一个NTAccount对象描述了一个权限可以分配的安全主体。在实践中,它是用户和组。NTAccount对象可以使用Translate()来输出它包含的与主体对应的SID。而这只会在指定的账号确实存在的情况下有效。否则,你会得到一个错误。因此你也可以使用Translate()来验证一个账号的存在性。

通过Translate()获取的SID非常有用。如果你仔细看,你会发现管理员组的SID和你自己当前账号的SID完全不同:

([System.Security.Principal.NTAccount]"$env:userdomain\$env:username").`
Translate([System.Security.Principal.Securityidentifier]).Value

S-1-5-21-2146773085-903363285-719344707-1282827

([System.Security.Principal.NTAccount]"Administrators").`
Translate([System.Security.Principal.Securityidentifier]).Value

S-1-5-32-544

管理员组的SID不但很短,而且是唯一的。为了整合这个账号,Windows使用了所谓的众所周知的SID,它在所有的Windows系统中都是相同的。这一点很重要,因为你德文系统中运行上面的脚本会失败。在本地化的德文系统上,因为Administrators组叫做”Administratoren”,”Everyone”组叫做”Jeder”。但是这些账号的SID是相同的。知道了这些组的SID号,你就可以使用它们代替那些本地化的名称了。下面是怎样将SID转换成用户账号的名称:

$sid = [System.Security.Principal.SecurityIdentifier]"S-1-1-0"
$sid.Translate([System.Security.Principal.NTAccount])

Value
-----
Everyone

这是怎样让你的脚本能够非常完美地在国际本地化机器上运行:

Del Protected
Md Protected
$acl = Get-Acl Protected

# 管理员完全控制:
$sid = [System.Security.Principal.SecurityIdentifier]"S-1-5-32-544"
$access = [System.Security.AccessControl.FileSystemRights]"FullControl"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( `
$sid,$access,3,0,0)
$acl.AddAccessRule($rule)

# 所有用户的只读权限:
$sid = [System.Security.Principal.SecurityIdentifier]"S-1-1-0"
$access = [System.Security.AccessControl.FileSystemRights]"ReadAndExecute"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule( `
$sid,$access,3,0,0)
$acl.AddAccessRule($rule)

# 保存权限更新:
Set-Acl Protected $acl

原文地址:Working with the File System

本文链接: https://www.pstips.net/managing-access-permissions.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

3 条评论 “PowerShell文件系统(五)管理访问权限