利用特殊权限保护 Linux 文件系统的最佳实践

掌握 Linux 文件系统安全性,通过利用特殊权限:SUID、SGID 和粘滞位。本指南解释了如何使用八进制表示法安全地应用这些模式,以强制执行上下文、确保共享文件夹中的组继承,并防止在 /tmp 等目录中未经授权的文件删除,提供了系统加固的实用示例。

利用特殊权限保护 Linux 文件系统的最佳实践

特殊权限是 Linux 中那些在 ls -l 中看起来很小,但在生产环境中却非常重要的特性之一。一个额外的 s 在 root 拥有的二进制文件上可以让普通用户执行特定的特权任务。一个缺失的 t 在共享上传目录上可以让用户删除彼此的文件。一个 SGID 位在项目目录上可以节省你处理组所有权问题的麻烦。

目标不是到处设置特殊位。目标是知道何时 SUID、SGID 和粘滞位能解决实际的访问问题,以及何时它们会创建你以后会后悔的特权路径。

理解标准权限回顾

在探讨特殊权限之前,必须记住标准的三元组表示法(所有者、组和其他用户的 rwx)。这些权限使用八进制数值表示(例如 755644)。

  • r(读)= 4
  • w(写)= 2
  • x(执行)= 1

特殊权限修改了这种基本行为,并由一个前导的第四位八进制数字(4、2 或 1)表示。

特殊权限:SUID、SGID 和粘滞位

特殊权限增加了超出标准访问控制的功能。它们通常在长列表(ls -l)输出中用 st 替换标准的 x 标志来表示。

权限 八进制值 效果
SUID(设置用户 ID) 4 文件以文件所有者的权限执行(而不是运行它的用户)。
SGID(设置组 ID) 2 文件以文件组的权限执行,或者新文件继承父目录的组 ID。
粘滞位 1 防止用户在共享目录中删除或重命名其他用户拥有的文件,即使他们对目录具有写权限。

1. 设置用户 ID (SUID)

SUID 位功能强大,但如果使用不当则可能很危险。当在可执行文件上设置时,任何执行该文件的用户都将以所有者的权限运行该进程。

实际用例: 需要 root 权限执行特定任务,但不应授予用户一般 root 访问权限的实用程序。

示例:/usr/bin/passwd 上的 SUID

/usr/bin/passwd 命令通常需要 root 权限来修改安全的 /etc/shadow 文件。它设置了 SUID 位,允许普通用户在执行 passwd 更改自己的密码期间,临时获得所有者(root)的权限。

查看 SUID: 注意所有者的执行位置中的 s

ls -l /usr/bin/passwd
# 输出示例:-rwsr-xr-x 1 root root ... /usr/bin/passwd

设置 SUID: 使用八进制值 4 结合标准权限(例如 755 变为 4755):

# 假设 'my_script' 由 'appuser' 拥有
chmod 4755 /path/to/my_script

SUID 的安全警告: 切勿在通用 shell(如 /bin/bash)或解释外部输入的广泛维护脚本上设置 SUID 位。如果脚本需要特权,窄范围的 sudoers 规则、经过审查的小型辅助程序或服务 API 通常更安全。

当你审计主机时,SUID 文件值得关注,因为它们是有意的特权交叉点:

sudo find / -xdev -type f -perm -4000 -ls 2>/dev/null

-xdev 将扫描限制在当前文件系统,当 /proc、挂载的备份或网络文件系统会使结果混乱时,这很有用。在具有单独挂载点的服务器上,有意地扫描每个实际文件系统。

2. 设置组 ID (SGID)

SGID 位有两个主要功能,取决于它是应用于文件还是目录。

A. 可执行文件上的 SGID

当在可执行文件上设置时,进程以文件组所有权关联的权限运行,而不是用户的 primary 组。

B. 目录上的 SGID(对共享环境至关重要)

当在目录上设置时,在其中创建的任何新文件或子目录都会自动继承父目录的组所有权,而不是创建新文件的用户的 primary 组。

实际用例: 所有贡献者必须具有统一组访问权限以进行协作的共享项目文件夹。

在目录上设置 SGID: 使用八进制值 2 结合标准权限(例如 775 变为 2775):

# 将组所有权设置为 'developers' 并启用 SGID
chgrp developers /srv/shared_project
chmod 2775 /srv/shared_project

这处理了组继承,但不能保证每个新文件都是组可写的。用户的 umask 仍然重要。如果贡献者使用限制性的 umask(如 077)创建文件,文件可能会继承 developers 组,但组仍然无法读取或写入。对于共享目录,请检查预期的 umask、默认 ACL 或应用程序级别的文件创建设置:

getfacl /srv/shared_project
setfacl -d -m g:developers:rwx /srv/shared_project

默认 ACL 通常是协作目录中缺失的部分,因为它们将权限应用于路径下创建的新文件和目录。

3. 粘滞位

粘滞位(或保存文本属性)几乎专门用于共享目录以控制文件删除。

当在目录上设置粘滞位时,只有该目录中文件的所有者root 用户才能删除或重命名该文件,即使目录本身允许'其他用户'写入(o+w)

实际用例: 公共共享目录如 /tmp 或部门上传文件夹,用户应只能管理自己创建的文件。

示例:/tmp 目录

/tmp 目录通常具有 1777 权限。1 表示粘滞位已激活。

ls -ld /tmp
# 输出示例:drwxrwxrwt 15 root root 4096 Mar 10 11:30 /tmp

末尾的 t 确认粘滞位已设置。如果没有它,任何用户都可以删除 /tmp 中其他用户创建的文件。

设置粘滞位: 使用八进制值 1 结合标准权限(例如 777 变为 1777):

chmod 1777 /var/public_uploads

综合管理:组合特殊权限

特殊权限通常组合使用。第四位前导数字是所需特殊位的总和(4+2+1 = 7)。

所需权限 八进制值
标准 rwxr-xr-x (755) 755
SUID + rwxr-xr-x 4755
SGID + rwxr-xr-x 2755
粘滞位 + rwxrwxrwx 1777
SUID + SGID + 粘滞位 + rwx(很少需要) 7777

组合 SGID 和粘滞位用于共享文件夹的示例:

要创建一个安全的共享协作目录,其中所有用户都是 'team' 组的成员,新文件继承 'team' 组,并且用户不能删除彼此的文件:

  1. 设置组所有权:chgrp team /data/projectX
  2. 应用 SGID (2) + 标准 rwx (7) + 粘滞位 (1) $\rightarrow 2+1 = 3$ 作为特殊位。
    • 使用显式求和:SGID (2) + 粘滞位 (1) = 3。标准权限 775
    • 完整命令:chmod 3775 /data/projectX

使用 ls -ld 查看时,你应该在组执行位置看到 s,在其他用户执行位置看到 t

drwxrwsr-t 2 root team 4096 Mar 10 11:30 /data/projectX

如果缺少执行位,显示将使用大写 ST。这通常是目录上的警告信号,因为执行权限控制用户是否可以遍历目录。

导致实际事件的常见错误

最危险的错误是使用 SUID 来避免设计适当的授权。如果维护脚本需要轮换一个应用程序拥有的文件,不要使整个脚本对每个用户都有效 root。给应用程序一个受控的服务命令,使用窄范围的 sudoers 规则,或更改所有权,使服务帐户无需 root 即可执行任务。

另一个常见错误是使共享目录全局可写而不设置粘滞位:

chmod 777 /srv/uploads

这在测试期间看起来很便捷。在生产环境中,任何可以访问该目录的本地用户都可能重命名或删除其他用户的文件。如果目录确实必须由许多不相关的用户写入,1777 通常是更安全的基础:

chmod 1777 /srv/uploads

对于团队拥有的目录,2775 加上实际组通常比全局写入更好。仅当团队成员不应删除彼此的文件时才添加粘滞位。有些团队确实希望共享清理权限;其他团队则不希望。权限模型应与该工作流程匹配。

备份和部署也可能破坏特殊权限。某些归档和复制工具默认保留模式;其他工具则不会,除非你传递正确的标志。在恢复系统或部署二进制包后,显式验证特殊位:

stat -c '%A %a %U %G %n' /usr/bin/passwd /tmp /srv/shared_project

如果 /tmp 返回 0777 而不是 1777,这不是外观上的差异。用户可以干扰彼此的文件。如果系统实用程序上缺少所需的 SUID 位,普通用户可能会突然失去预期的行为。如果意外的 SUID 位出现在 /home/tmp 或应用程序上传路径中的文件上,则将其视为可疑,直到证明其安全。

审计而不被输出淹没

完整的文件系统扫描可能会很嘈杂,尤其是在构建机器和开发人员工作站上。从最重要的区域开始:

sudo find /usr /bin /sbin /opt -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null
sudo find /tmp /var/tmp /dev/shm -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null

第一个命令检查正常的可执行文件位置。第二个命令检查可写的临时区域,其中特权可执行文件会更令人担忧。在干净的服务器上,全局可写路径中的自定义 SUID 文件应该很少见。如果发现一个,请检查所有权、包来源、修改时间以及它是否出现在你的配置管理中。

对于目录,查找没有粘滞位的全局可写路径:

sudo find / -xdev -type d -perm -0002 ! -perm -1000 -ls 2>/dev/null

这并不意味着每个结果都是漏洞。这意味着每个结果都需要一个理由。某些应用程序目录有意由服务组写入,但没有粘滞位保护的公共可写目录是常见的隐患。

特殊权限安全的最佳实践

由于 SUID 和 SGID 授予提升的权限,必须极其谨慎地管理它们。

  1. 限制 SUID 范围: 仅在标准操作所需的编译二进制可执行文件上设置 SUID(如 passwdping)。切勿将 SUID 应用于解释型脚本(Shell、Python、Perl),除非它们被包装在验证输入的安全包装器可执行文件中。
  2. 定期审计: 使用 find 命令定期扫描文件系统以查找异常的 SUID/SGID 文件:
    # 查找所有设置了 SUID 的文件
    find / -perm /4000 2>/dev/null
    # 查找所有设置了 SGID 的文件
    find / -perm /2000 2>/dev/null
    
  3. 使用 SGID 实现组一致性: 在共享数据结构中,优先使用 SGID 而不是手动管理文件组所有权;它可以自动化组继承。
  4. 粘滞位用于公共可写区域: 对于任何旨在供一般用户使用且必须限制非所有者删除的目录(例如 /tmp/var/tmp),粘滞位是必不可少的。
  5. 当协作重要时,将 SGID 与 ACL 配对: SGID 处理组所有权;默认 ACL 处理新文件接收的权限。
  6. 跟踪自定义例外: 如果你的组织批准了自定义的 SUID 或 SGID 二进制文件,请记录其用途、所有者、预期路径和部署来源。神秘的特权是以后会带来麻烦的部分。

特殊权限之所以有用,是因为它们精确。保持这种状态。使用 SUID 进行窄范围的特权转换,使用 SGID 进行共享所有权,使用粘滞位用于需要保护删除权限的共享目录。