DAC(RWX)
目录
- 概述:什么是 DAC
- DAC 的核心模型:主体-客体-规则
- UGO 权限模型详解
- 特殊权限位:SUID、SGID、Sticky Bit
- 进程凭证:内核中的身份表示
- 内核中的 DAC 权限检查流程
- ACL:突破 UGO 的局限
- 实战命令大全
- DAC 的安全局限与 MAC 的补充
- 最佳实践
- 总结
1. 概述:什么是 DAC
DAC(Discretionary Access Control,自主访问控制)是 Linux 最基础、最核心的权限控制机制。它的核心思想是:资源的所有者可以自主决定谁可以访问该资源。
DAC 在 Linux 安全体系中的位置
用户进程请求访问文件
│
▼
┌─────────────────────────────┐
│ 第一道防线:DAC │ ← 本文主题
│ ├─ UGO 权限检查 (rwx) │ 传统 9 位权限
│ ├─ ACL 扩展权限 │ 细粒度访问控制
│ └─ Capabilities 特权绕过 │ CAP_DAC_OVERRIDE 等
└─────────────┬───────────────┘
│ DAC 通过
▼
┌─────────────────────────────┐
│ 第二道防线:MAC │
│ ├─ SELinux (标签级) │ 强制访问控制
│ └─ AppArmor (路径级) │ 补充 DAC 不足
└─────────────┬───────────────┘
│ MAC 通过
▼
执行操作
关键特性:DAC 是 Linux 访问控制的第一道防线。如果 DAC 拒绝了访问,MAC(SELinux/AppArmor)根本不会被触发。DAC 先执行,MAC 后执行。
DAC 与 MAC 的核心区别
| 维度 | DAC(自主访问控制) | MAC(强制访问控制) |
|---|---|---|
| 决策者 | 资源所有者 | 系统管理员(全局策略) |
| 灵活性 | 高:用户可以自由授权 | 低:全局策略强制执行 |
| Root 权限 | Root 可以绕过 | Root 也可能被限制 |
| 典型实现 | UGO + ACL | SELinux、AppArmor |
| 粒度 | 用户/组级别 | 进程域/文件类型级别 |
2. DAC 的核心模型:主体-客体-规则
DAC 基于一个简洁的主体-客体-规则模型:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 主体 │ │ 规则 │ │ 客体 │
│ (Subject) │──────►│ (Policy) │──────►│ (Object) │
│ │ 请求 │ │ 判定 │ │
│ 进程 │ │ UGO + ACL │ │ 文件/目录 │
│ 凭证: │ │ │ │ 属主: uid │
│ uid/gid │ │ │ │ 属组: gid │
│ euid/fsuid │ │ │ │ 权限: mode │
└──────────────┘ └──────────────┘ └──────────────┘
三大要素
| 要素 | 定义 | DAC 中的具体实现 |
|---|---|---|
| 主体(Subject) | 发起访问请求的实体 | 进程,其身份由 euid、egid、fsuid 等凭证标识 |
| 客体(Object) | 被访问的目标资源 | 文件、目录、设备等,其身份由 uid、gid、mode 标识 |
| 规则(Policy) | 判定是否允许访问的规则 | UGO 权限位 + ACL 扩展规则 |
DAC 的核心特点:自主性
文件所有者 (alice) 创建了 report.txt:
-rw-r--r-- alice devteam report.txt
alice 可以自主决定:
├─ chmod g+w report.txt → 让同组用户可写
├─ chmod o-r report.txt → 禁止其他人读取
├─ setfacl -m u:bob:rw ... → 单独授权给 bob
└─ chown bob report.txt → 转让所有权给 bob
这就是"自主"的含义——所有者决定权限。
3. UGO 权限模型详解
3.1 User、Group、Other 三类主体
UGO 将访问者分为三类:
文件的权限位(共 9 位):
┌─── User ──┐ ┌── Group ──┐ ┌── Other ──┐
│ r w x │ │ r w x │ │ r w x │
└─────────────┘ └───────────┘ └───────────┘
所有者权限 所属组权限 其他人权限
三类主体的判定优先级(短路匹配):
1. User:进程的 fsuid == 文件的 uid → 应用 User 权限
2. Group:进程属于文件的 gid 所在组 → 应用 Group 权限
3. Other:以上都不匹配 → 应用 Other 权限
3.2 rwx 权限详解
对文件的权限
| 权限 | 字母 | 八进制 | 含义 |
|---|---|---|---|
| 读 | r |
4 | 读取文件内容 (cat, less, cp) |
| 写 | w |
2 | 修改文件内容 (vim, >, >>) |
| 执行 | x |
1 | 作为程序/脚本执行 (./script.sh) |
对目录的权限
| 权限 | 字母 | 八进制 | 含义 |
|---|---|---|---|
| 读 | r |
4 | 列出目录内容 (ls) |
| 写 | w |
2 | 在目录中创建/删除/重命名文件 |
| 执行 | x |
1 | 进入目录 (cd);访问目录中的文件 |
目录权限的特殊性——路径遍历:
要访问 /a/b/c.txt,需要:
├─ / 有 x 权限
├─ /a 有 x 权限
├─ /a/b 有 x 权限
└─ /a/b/c.txt 有 r 权限
缺少任何一级的 x 权限都会导致 "Permission denied"!
dr--r--r-- → 可以 ls 列出文件名,但不能 cd 进入
d--x--x--x → 可以 cd 进入并访问已知文件,但不能 ls 列出
drwxr-xr-x → 正常的可读写可进入目录
3.3 权限的八进制表示
# 每种权限对应一个数字:r=4, w=2, x=1
rwx = 4+2+1 = 7 rw- = 4+2+0 = 6 r-x = 4+0+1 = 5
r-- = 4+0+0 = 4 -w- = 0+2+0 = 2 --x = 0+0+1 = 1
--- = 0+0+0 = 0
# 完整权限示例
chmod 755 script.sh # rwxr-xr-x(可执行文件标准权限)
chmod 644 data.txt # rw-r--r--(数据文件标准权限)
chmod 600 secret.key # rw-------(私密文件)
chmod 777 public/ # rwxrwxrwx(危险!所有人都能读写)
# 符号模式(更直观)
chmod u+x script.sh # 给所有者加执行权限 (user +x)
chmod g-w data.txt # 移除组的写权限 (group -w)
chmod o= secret.key # 清空其他人的所有权限 (other =)
chmod a+r public.txt # 给所有人加读权限 (all +r)
chmod u=rwx,g=rx,o= app # 设置精确权限
3.4 umask:默认权限掩码
umask 控制新创建文件和目录的默认权限:
# 查看当前 umask
umask # 0022(常见默认值)
umask -S # u=rwx,g=rx,o=rx(符号模式显示)
# umask 计算原理
# 文件默认 = 0666 - umask
# 目录默认 = 0777 - umask
umask 0022:文件 0666-0022=0644 (rw-r--r--) 目录 0777-0022=0755 (rwxr-xr-x)
umask 0002:文件 0666-0002=0664 (rw-rw-r--) 目录 0777-0002=0775 (rwxrwxr-x)
umask 0077:文件 0666-0077=0600 (rw-------) 目录 0777-0077=0700 (rwx------)
# 临时修改
umask 0027
# 永久修改
echo "umask 0027" >> ~/.bashrc
4. 特殊权限位:SUID、SGID、Sticky Bit
4.1 SUID(Set User ID)— 八进制值 4
作用于可执行文件。执行时,进程的有效 UID 变为文件所有者的 UID。
# 经典示例:passwd 命令
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root /usr/bin/passwd
# ↑ s 在所有者 x 位 → SUID 已设置
# passwd 需要修改 /etc/shadow(仅 root 可写)
# 普通用户通过 SUID 临时获得 root 的 euid 来修改自己的密码
# 设置 SUID
chmod u+s /path/to/program
chmod 4755 /path/to/program
# 查看所有 SUID 文件
find / -perm -4000 -type f 2>/dev/null
# ⚠️ 危险示例:不要给 shell 设置 SUID!
# chmod u+s /bin/bash ← 任何用户执行 bash 都获得 root shell
4.2 SGID(Set Group ID)— 八进制值 2
作用于可执行文件:执行时进程的 egid 变为文件所属组。
作用于目录:目录中新建文件的所属组继承目录的组。
# SGID 作用于目录——共享项目目录的经典用法
mkdir /shared/project
chgrp devteam /shared/project
chmod 2770 /shared/project # SGID + rwxrwx---
ls -ld /shared/project
# drwxrws--- 2 root devteam /shared/project
# ↑ s 在组 x 位 → SGID 已设置
# 任何 devteam 成员在该目录创建的文件都属于 devteam 组
4.3 Sticky Bit(粘滞位)— 八进制值 1
仅作用于目录。目录中的文件只能被文件所有者或 root 删除。
# 经典应用:/tmp 目录
ls -ld /tmp
# drwxrwxrwt 1 root root /tmp
# ↑ t 在 other x 位 → Sticky Bit 已设置
# /tmp:所有人都可以创建文件,但只能删除自己的文件
chmod +t /shared/scratch
chmod 1777 /shared/scratch
4.4 特殊权限位的显示
# 大小写含义:
# SUID:s (有执行权限时) 或 S (无执行权限时)
# SGID:s (有执行权限时) 或 S (无执行权限时)
# Sticky:t (有执行权限时) 或 T (无执行权限时)
-rwsr-xr-x → SUID + 所有者有 x
-rwSr--r-- → SUID + 所有者无 x (大写 S 警告)
drwxrws--- → SGID + 组有 x
drwxrwxrwt → Sticky + other 有 x
5. 进程凭证:内核中的身份表示
5.1 struct cred 与四种 UID
内核不识别用户名,只识别 UID。每个进程通过 struct cred 维护其安全凭证:
// 简化的 struct cred(内核源码:include/linux/cred.h)
struct cred {
kuid_t uid; /* 实际 UID (real UID) — 进程的真实身份 */
kgid_t gid; /* 实际 GID */
kuid_t euid; /* 有效 UID (effective UID) — 权限检查的核心 */
kgid_t egid; /* 有效 GID */
kuid_t suid; /* 保存的 UID (saved UID) — 暂存切换前的 euid */
kgid_t sgid; /* 保存的 GID */
kuid_t fsuid; /* 文件系统 UID — 用于 VFS 操作的权限检查 */
kgid_t fsgid; /* 文件系统 GID */
// ...
};
| 字段 | 全称 | 作用 |
|---|---|---|
| uid | Real UID | 进程的真实身份,通常是登录用户的 UID |
| euid | Effective UID | 权限检查的核心依据,决定进程能以谁的身份操作 |
| suid | Saved UID | 保存 euid 被修改前的值,用于权限恢复 |
| fsuid | Filesystem UID | Linux 特有,专门用于文件系统操作的权限检查 |
通常情况下:
uid == euid == suid == fsuid
SUID 程序执行后:
uid = 1000 (alice 的真实身份)
euid = 0 (临时提升为 root)
suid = 0 (保存 root 身份,用于切换)
fsuid = 0 (文件系统操作也以 root 身份)
→ 进程以 root 权限运行,但真实身份仍是 alice
5.2 权限提升:SUID 的内核实现
// 内核在 execve() 时处理 SUID(简化版)
// 源码路径:fs/exec.c → bprm_fill_uid()
static void bprm_fill_uid(struct linux_binprm *bprm) {
// 检查文件是否设置了 SUID 位
if (bprm->file->f_inode->i_mode & S_ISUID) {
// 将新进程的 euid 设置为文件所有者的 uid
bprm->cred->euid = bprm->file->f_inode->i_uid;
bprm->cred->fsuid = bprm->file->f_inode->i_uid;
}
// 检查文件是否设置了 SGID 位
if (bprm->file->f_inode->i_mode & S_ISGID) {
bprm->cred->egid = bprm->file->f_inode->i_gid;
}
}
5.3 权限降级:setuid 系列系统调用
// 三种 setuid 系统调用
// 1. setuid() — 简单设置
// Root:可设为任意值,且 uid/euid/suid/fsuid 全部被设置
// 非 root:只能设为 uid 或 euid 或 suid 中的值
int setuid(uid_t uid);
// 2. setreuid() — 分别设置 real uid 和 effective uid
// 非 root:只能将 ruid 设为 ruid 或 euid,将 euid 设为 ruid/euid/suid
int setreuid(uid_t ruid, uid_t euid);
// 3. setresuid() — 精确设置三个 UID(最灵活)
// 非 root:每个参数只能设为当前 ruid/euid/suid 中的值
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
// 典型用法(sudo 等工具):
// 1. 临时提升到 root → setresuid(-1, 0, -1)
// 2. 完成操作后降级 → setresuid(target_uid, target_uid, target_uid)
6. 内核中的 DAC 权限检查流程
6.1 VFS 层的权限检查路径
以 open("/a/b/c.txt", O_RDONLY) 为例:
用户空间:open("/a/b/c.txt", O_RDONLY)
│
▼
VFS:do_sys_open()
│
▼
路径遍历——对每一级目录检查 x 权限
│
├─ may_lookup("/") → 检查 / 的 x 权限
├─ may_lookup("/a") → 检查 /a 的 x 权限
├─ may_lookup("/a/b") → 检查 /a/b 的 x 权限
│
▼
may_open("/a/b/c.txt", O_RDONLY)
│
├─ 检查 O_RDONLY → 需要 r 权限
├─ 检查 O_WRONLY → 需要 w 权限
├─ 检查 O_TRUNC → 需要 w 权限
└─ 检查 O_APPEND → 需要 w 权限
│
▼
inode_permission(inode, MAY_READ)
│
├─ generic_permission() → UGO 检查
└─ acl_permission_check() → ACL 检查
6.2 acl_permission_check() 核心函数
// 内核源码:fs/namei.c(简化版)
static int acl_permission_check(struct inode *inode, int mask)
{
unsigned int mode = inode->i_mode;
// 1. Root 特权检查
if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
return capable(CAP_DAC_OVERRIDE) ? 0 : -EACCES;
// 如果 fsuid==0 且有 CAP_DAC_OVERRIDE → 直接放行
// 2. User 匹配:进程 fsuid == 文件 uid
if (uid_eq(current_fsuid(), inode->i_uid)) {
mode >>= 6; // 取 owner 权限位
goto check;
}
// 3. ACL 检查(如果存在)
if (IS_POSIXACL(inode)) {
int error = posix_acl_permission(inode, current_fsuid(), mask);
if (error != -EAGAIN)
return error; // ACL 明确允许或拒绝
}
// 4. Group 匹配:进程属于文件 gid 所在组
if (in_group_p(inode->i_gid)) {
mode >>= 3; // 取 group 权限位
goto check;
}
// 5. Other:以上都不匹配
// mode 保持为 other 权限位
check:
// 检查 mask 要求的权限是否在 mode 中
if ((mask & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0)
return 0; // 权限足够
return -EACCES;
}
6.3 DAC 与 MAC 的执行顺序
// 完整的文件访问检查流程(简化)
int do_file_open(const char *pathname, int flags) {
struct inode *inode;
// 步骤 1:DAC 检查
int error = inode_permission(inode, acc_mode);
if (error)
return error; // DAC 拒绝 → 直接返回,MAC 不执行
// 步骤 2:MAC 检查(LSM Hook)
error = security_inode_permission(inode, acc_mode);
if (error)
return error; // SELinux/AppArmor 拒绝
// 步骤 3:执行实际操作
return do_actual_open(inode, flags);
}
关键顺序:DAC → MAC。如果 DAC 拒绝了,MAC 检查被跳过。这就是为什么 SELinux 的 AVC 日志只在 DAC 允许后才出现。
7. ACL:突破 UGO 的局限
7.1 为什么需要 ACL
UGO 模型只能为一个文件设置一个所有者、一个组、一个"其他人",无法为多个特定用户设置不同权限:
问题场景:
report.txt 属于 alice:devteam
- alice 需要 rw-(读写)
- bob 需要 r--(只读)
- carol 需要 ---(禁止访问)
- devteam 组需要 r--(只读)
UGO 无法实现!因为:
├─ User 只能匹配一个用户(alice)
├─ Group 只能匹配一个组(devteam)
└─ bob 和 carol 要么在 devteam 组,要么在 Other
ACL 解决方案:
setfacl -m u:bob:r-- report.txt # bob 只读
setfacl -m u:carol:--- report.txt # carol 禁止
setfacl -m g:devteam:r-- report.txt # devteam 只读
7.2 ACL 条目类型
# 查看 ACL
getfacl report.txt
# 输出示例:
# file: report.txt
# owner: alice
# group: devteam
user::rw- # 所有者 ACL(对应 UGO 的 User)
user:bob:r-- # 指定用户 ACL
user:carol:--- # 指定用户 ACL
group::r-- # 所属组 ACL(对应 UGO 的 Group)
group:auditors:r-- # 指定组 ACL
mask::r-- # ACL Mask(有效权限上限)
other::--- # 其他人 ACL(对应 UGO 的 Other)
| 条目类型 | 语法 | 对应 |
|---|---|---|
| 所有者 | user::rwx |
UGO 的 User 权限 |
| 指定用户 | user:alice:rw- |
ACL 扩展 |
| 所属组 | group::r-x |
UGO 的 Group 权限 |
| 指定组 | group:devteam:r-- |
ACL 扩展 |
| Mask | mask::r-x |
有效权限上限 |
| 其他人 | other::r-- |
UGO 的 Other 权限 |
7.3 ACL Mask 与有效权限
ACL Mask 是 ACL 中最重要的概念之一——它是所有指定用户、指定组和所属组的有效权限上限:
# 设置 ACL
setfacl -m u:bob:rwx report.txt
# 查看
getfacl report.txt
# user:bob:rwx
# mask::r-- ← Mask 只有 r--
#
# 实际有效权限:bob 只有 r--(被 mask 限制)
# 显示为:user:bob:rwx #effective:r--
# 原理:
# 指定用户/组的 实际权限 = 设置的权限 ∩ Mask
# rwx ∩ r-- = r-- → bob 实际只有读权限
# 更新 Mask
setfacl -m mask::rwx report.txt
# bob 现在有完整的 rwx 了
Mask 自动重算规则:
当使用 setfacl 添加/修改/删除 ACL 条目时,
Mask 会自动重新计算为:
max(所有指定用户权限, 所有指定组权限, 所属组权限)
7.4 Default ACL:目录继承
Default ACL 让目录中的新创建文件和子目录自动继承 ACL:
# 设置 Default ACL
setfacl -m d:u:bob:r-- /shared/project
setfacl -m d:g:devteam:rwx /shared/project
# 查看
getfacl /shared/project
# user::rwx
# group::r-x
# other::---
# default:user::rwx
# default:user:bob:r--
# default:group::r-x
# default:group:devteam:rwx
# default:mask::rwx
# default:other::---
# 新建文件自动继承
touch /shared/project/newfile.txt
getfacl /shared/project/newfile.txt
# user::rw-
# user:bob:r-- ← 自动继承
# group::r-x ← 自动继承
# group:devteam:rwx ← 自动继承
7.5 getfacl 与 setfacl 实战
# ========== getfacl:查看 ACL ==========
getfacl file.txt # 查看单个文件
getfacl -R /shared/project/ # 递归查看目录
getfacl -t file.txt # 表格形式输出
getfacl -c file.txt # 简洁模式(不显示注释)
getfacl --omit-header file.txt # 不显示头部
# ========== setfacl:设置 ACL ==========
# 基本语法:setfacl -m <条目> <文件>
# 添加用户权限
setfacl -m u:bob:rw- report.txt # bob 读写
setfacl -m u:carol:r-- report.txt # carol 只读
# 添加组权限
setfacl -m g:auditors:r-- report.txt
# 修改权限
setfacl -m u:bob:rwx report.txt # 修改 bob 的权限
# 设置 Mask
setfacl -m mask::rwx report.txt
# 删除 ACL 条目
setfacl -x u:bob report.txt # 删除 bob 的条目
setfacl -x g:auditors report.txt # 删除 auditors 组的条目
# 删除所有 ACL
setfacl -b report.txt # 删除所有扩展 ACL
setfacl -k /shared/project/ # 删除所有 Default ACL
# 递归操作
setfacl -R -m u:bob:r-- /shared/ # 递归设置
# 从文件读取 ACL
getfacl source.txt | setfacl --set-file=- target.txt # 复制 ACL
8. 实战命令大全
8.1 基础权限管理
# 查看权限
ls -l file.txt # 详细列表
ls -ld /etc # 目录本身而非内容
stat file.txt # 更详细的信息(含八进制权限)
# 修改权限
chmod 755 script.sh # 八进制
chmod u+x,g+w,o-r file.txt # 符号模式
chmod -R 755 /var/www/ # 递归修改
# 修改所有者和组
chown alice file.txt # 改所有者
chown alice:devteam file.txt # 改所有者和组
chown :devteam file.txt # 仅改组
chgrp devteam file.txt # 改组
chown -R alice:devteam /var/www/ # 递归
# 查看用户和组信息
id alice # 用户 UID 和所属组
groups alice # 用户的所有组
getent passwd alice # passwd 条目
getent group devteam # group 条目
8.2 特殊权限管理
# 查找特殊权限文件
find / -perm -4000 -type f 2>/dev/null # SUID
find / -perm -2000 -type f 2>/dev/null # SGID
find / -perm -1000 -type d 2>/dev/null # Sticky Bit
find / -perm -6000 -type f 2>/dev/null # SUID + SGID
# 设置
chmod u+s /usr/bin/myapp # SUID
chmod g+s /shared/dir # SGID
chmod +t /shared/scratch # Sticky Bit
# 移除
chmod u-s /usr/bin/myapp # 移除 SUID
chmod g-s /shared/dir # 移除 SGID
chmod -t /shared/scratch # 移除 Sticky Bit
8.3 ACL 管理
# 查看
getfacl file.txt
# 设置
setfacl -m u:alice:rw file.txt
setfacl -m g:devteam:r file.txt
setfacl -m d:u:bob:r /shared/ # Default ACL
# 删除
setfacl -x u:alice file.txt # 删除条目
setfacl -b file.txt # 删除所有扩展 ACL
# 批量备份和恢复
getfacl -R /important/ > acl_backup.txt
setfacl --restore=acl_backup.txt
8.4 权限诊断与调试
# 模拟权限检查
namei -l /path/to/file
# 输出每一级目录的权限
# 测试文件是否可读/可写/可执行
test -r /path/to/file && echo "readable"
test -w /path/to/file && echo "writable"
test -x /path/to/file && echo "executable"
# 以特定用户身份测试
sudo -u alice test -r /path/to/file && echo "alice can read"
sudo -u alice cat /path/to/file
# 查看进程的实际身份
ps -eo pid,user,group,comm
cat /proc/$$/status | grep -E "^Uid|^Gid"
# 审计文件权限问题
find /home -type f -perm 0777 # 查找权限过宽的文件
find / -nouser -o -nogroup # 查找无主文件
find / -perm -4000 -type f -ls # 审计 SUID 文件
9. DAC 的安全局限与 MAC 的补充
DAC 的主要局限
| 局限 | 说明 | 示例 |
|---|---|---|
| 粒度粗 | UGO 只有三类主体 | 无法给第 4 个用户单独设权限(需 ACL) |
| Root 全能 | Root 可以绕过所有 DAC 检查 | 被攻破的 root 进程可以读取任何文件 |
| 无法防恶意软件 | 用户运行的恶意程序继承用户的所有权限 | 恶意脚本可以删除用户的所有文件 |
| 用户可自由授权 | 用户可能不小心给了过多权限 | chmod 777 important_data/ |
| 无法基于进程类型限制 | DAC 只看 UID,不看程序是什么 | 不能限制"nginx 只能读 /var/www" |
| 硬链接可能绕过 | 同一个 inode 可以通过不同路径访问 | 无权限用户可以通过硬链接访问受保护文件 |
MAC 如何弥补 DAC 的不足
DAC 场景:alice 运行了一个有漏洞的 Web 服务器
→ Web 服务器以 alice 身份运行
→ 漏洞被利用后,攻击者可以读取 alice 的所有文件
→ DAC 无法阻止(进程就是 alice 的身份)
MAC 场景:同样的漏洞
→ SELinux 策略:httpd_t 域只能读 httpd_sys_content_t 类型
→ 即使进程以 alice 身份运行,SELinux 仍然限制它
→ 攻击者无法访问 alice 的其他文件
完整的 Linux 权限检查链
┌─────────────────────────┐
进程请求访问文件 │ 1. DAC (自主访问控制) │
│ │ ├─ UGO 权限位 │
▼ │ ├─ ACL 扩展规则 │
┌───────┐ │ └─ Capabilities 特权绕过 │
│ DAC │── 拒绝 ──► 返回 EACCES │
└───┬───┘ └─────────────────────────┘
│ 通过
▼ ┌─────────────────────────┐
┌───────┐ │ 2. MAC (强制访问控制) │
│ MAC │── 拒绝 ──► │ ├─ SELinux (标签级) │
└───┬───┘ │ └─ AppArmor (路径级) │
│ 通过 └─────────────────────────┘
▼
执行操作
10. 最佳实践
10.1 权限设置原则
# ✅ 最小权限原则
chmod 600 secret.key # 只有所有者可读写
chmod 750 ~/scripts/ # 所有者 rwx,组 rx,其他人无权限
chmod 640 /etc/app/config.yml # 所有者 rw,组 r,其他人无
# ✅ 使用组而非单独授权
groupadd devteam
usermod -aG devteam alice bob carol
chgrp devteam /shared/project
chmod 770 /shared/project
# ✅ 需要单独授权时使用 ACL
setfacl -m u:contractor:r-- /shared/project/report.txt
# ❌ 永远不要这样做
chmod 777 important_data/ # 所有人可读写!
chmod -R 777 /var/www/ # Web 目录 777!
chmod u+s /bin/bash # Shell 的 SUID!
10.2 安全审计 Checklist
# 1. 检查全局可写目录
find / -type d -perm -002 -not -perm -1000 2>/dev/null
# 2. 检查无主文件
find / -nouser -o -nogroup 2>/dev/null
# 3. 审计 SUID/SGID 文件
find / -perm -6000 -type f 2>/dev/null > suid_audit.txt
# 4. 检查用户家目录权限
find /home -maxdepth 1 -type d -perm /027
# 5. 检查 SSH 相关文件的权限
ls -l /etc/ssh/ssh_host_*_key # 应为 600
ls -l ~/.ssh/id_* # 应为 600
ls -l ~/.ssh/authorized_keys # 应为 600
# 6. 定期审查 ACL
getcap -r / 2>/dev/null > caps_audit.txt
getfacl -R /etc /var 2>/dev/null | grep -E "^user:|^group:" | sort -u
10.3 常见配置模板
# Web 服务器目录
chmod 755 /var/www/ # 目录
chmod 644 /var/www/*.html # 静态文件
chmod 640 /var/www/app/config.php # 配置文件
# 用户家目录
chmod 700 /home/alice # 或 750
chmod 600 /home/alice/.ssh/id_rsa # 私钥
chmod 644 /home/alice/.ssh/id_rsa.pub # 公钥
chmod 600 /home/alice/.ssh/authorized_keys
# 共享项目目录
chmod 2770 /shared/project # SGID + 组权限
setfacl -m d:g:devteam:rwx /shared/project # 默认 ACL
11. 总结
核心要点
| 要点 | 说明 |
|---|---|
| DAC 是第一道防线 | 在所有访问控制中首先执行,DAC 拒绝则 MAC 不触发 |
| UGO 是基础 | User/Group/Other 三类主体 + rwx 三种权限 = 9 位权限位 |
| 四种 UID | uid/euid/suid/fsuid 共同决定进程的身份和权限 |
| SUID 提升权限 | 执行时 euid 变为文件所有者,是 sudo/su 的基础 |
| SGID 组继承 | 目录设置 SGID 后,新建文件自动继承目录的组 |
| Sticky Bit 保护 | /tmp 目录的经典应用,防止误删他人文件 |
| ACL 突破 UGO 限制 | 可为任意多个用户/组设置不同权限 |
| umask 控制默认权限 | 新文件的默认权限 = 0666 - umask |
| DAC + MAC 纵深防御 | DAC 提供基础控制,MAC 提供强制策略 |
推荐资源
- chmod(1) - Linux 手册页
- chown(1) - Linux 手册页
- setfacl(1) - Linux 手册页
- credentials(7) - Linux 手册页
- acl(5) - Linux 手册页
- 内核源码:fs/namei.c — VFS 权限检查
免责声明:本文内容基于 Linux 手册页和内核文档编写。具体行为可能因内核版本和发行版配置而异,请以实际运行环境为准。