本文目录导读:

严格限制执行权限是 Linux/Unix 系统安全的核心,一个被攻破的进程如果拥有过高的执行权限,攻击者就能轻易窃取数据、安装后门或破坏系统。
要做到“严格限制”,需要从最小权限原则、用户与组隔离、文件系统权限、Capabilities 机制、系统调用过滤以及容器/沙箱隔离等多个层面入手。
以下是核心策略和具体操作方法:
核心原则:最小权限原则
每个进程和用户只应拥有完成其任务所绝对必需的最小权限,不要给“可能用到的”权限,只给“现在就要用”的。
用户与组隔离
这是最基础也是最重要的一环。
-
不要使用 root 运行服务: 绝大多数应用(如 Nginx、MySQL、Java 应用)都不需要 root 权限,使用 root 运行是最大的安全隐患。
-
创建专用系统用户: 为每个服务创建独立的、没有 shell 登录权限的系统用户。
# 创建一个系统用户 nginx,不允许登录,没有主目录 sudo useradd -r -s /usr/sbin/nologin -M nginx # 创建一个系统用户 myapp sudo useradd -r -s /usr/sbin/nologin -M myapp
-
权限分离: 将不同功能模块分配到不同用户下。
webapp用户:运行 Web 应用代码(只读)。webapp-data用户:拥有上传目录(上传目录绝对不能有执行权限)。nginx用户:运行 Nginx 反向代理。
文件系统权限
严格使用 chmod、chown 控制谁可以读、写、执行文件。
-
精准的属主和属组:
# 应用代码属主为 root,属组为 webapp-group chown -R root:webapp-group /var/www/myapp/ # 应用代码目录权限:属主读写执行,属组读执行,其他人无权限 chmod -R 750 /var/www/myapp/ # 上传目录属主为 webapp-user chown -R webapp-user:webapp-group /var/www/myapp/uploads/ # 上传目录权限:属主读写执行,属组读执行,其他人无权,关键:移除组和其他人的写权限! chmod -R 750 /var/www/myapp/uploads/
-
不要使用 777: 永远不要设置
chmod 777,任何需要写权限的地方,都应该通过属主或属组来控制,而不是让“其他用户”都能写。 -
慎用 SUID/SGID/Sticky Bit:
- SUID (Set User ID): 极其危险,当一个设置了 SUID 位的可执行文件运行时,它会以文件所有者的权限运行,而不是当前用户。
passwd命令需要 SUID 才能修改/etc/shadow。除非绝对必要,否则不要给任何自定义程序或脚本设置 SUID。 - SGID (Set Group ID): 相对安全,但也要小心,它主要用于协作目录(让新创建的文件继承父目录的组)。
- Sticky Bit (粘滞位): 用于
/tmp之类的共享目录,防止用户删除不属于自己的文件(chmod +t /tmp)。
- SUID (Set User ID): 极其危险,当一个设置了 SUID 位的可执行文件运行时,它会以文件所有者的权限运行,而不是当前用户。
Linux Capabilities(能力机制)
这是传统 SUID 的现代替代方案,也是严格限制权限的最强大工具。
传统上,root 用户拥有一切能力(Capability),Capabilities 机制将 root 的超级权限分解成 40 多种独立的小权限(如 CAP_NET_BIND_SERVICE 允许绑定到低于 1024 的端口,CAP_DAC_OVERRIDE 允许绕过文件权限检查)。
使用方法: 你不需要给程序整个 root 权限,只需要给它一项或几项 Capability。
-
场景: 你的普通用户
webapp需要监听 80 端口(要求 root 权限)。 -
传统做法: 用 root 启动,然后降权(依然可能被利用)。
-
最佳做法: 直接赋予程序绑定低端口的 Capability。
# 给 nginx 二进制文件添加绑定特权端口的 Capability # 注意:要设置有效的 Capability (p e i 分别代表 Permitted, Effective, Inheritable) sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx # 验证 getcap /usr/sbin/nginx # 输出:/usr/sbin/nginx = cap_net_bind_service+ep
-
常用 Capabilities 及用途:
CAP_NET_BIND_SERVICE:绑定到小于 1024 的端口。CAP_DAC_OVERRIDE:绕过文件读写权限检查(非常危险)。CAP_CHOWN:修改文件属主(危险)。CAP_KILL:发送信号给任何进程。CAP_SETUID / CAP_SETGID:设置用户/组 ID。
系统调用过滤 (Seccomp)
这是内核层面的终极防御,Seccomp (Secure Computing Mode) 允许你限制一个进程可以使用的系统调用。
即使程序被攻破,攻击者也无法执行 fork()、execve()、open() 等危险系统调用来提权或执行新代码。
-
如何实现:
- 编程方式: 使用
prctl()或seccomp()系统调用编写 BPF (Berkeley Packet Filter) 规则。 - 工具库:
libseccomp提供了方便的 C API。 - Docker/容器: Docker 和 Podman 默认会为容器应用一个安全的 Seccomp 配置文件,你可以在
docker run时使用--security-opt seccomp=profile.json自定义配置文件。
- 编程方式: 使用
-
实践示例(Docker):
# 使用默认的 seccomp 配置文件(会阻止 44 个危险系统调用) docker run --security-opt seccomp=default.json alpine sh # 创建一个更严格的配置文件 my-seccomp.json,只允许 read, write, exit, getpid # 然后运行容器: docker run --security-opt seccomp=my-seccomp.json myapp
容器与沙箱隔离
容器(如 Docker、Podman)和沙箱(如 Firejail、Bubblewrap)通过 Namespace 和 Cgroups 提供了一层极强的隔离。
-
容器隔离: 每个容器有自己的 PID、网络、挂载点等 Namespace。
- 避免以
--privileged运行: 这会关闭所有隔离,赋予容器几乎与宿主机 root 同等的权限。 - 使用只读文件系统:
docker run --read-only myapp,将应用代码和数据目录挂载为只读,只有/tmp或上传目录可写。 - 禁用 Capabilities:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
- 避免以
-
沙箱隔离: Firejail 可以让你以普通用户运行任何程序,并限制其对文件系统、网络和进程的访问。
# 运行 Firefox,限制其访问家目录以外的文件 firejail --private=~/firefox-data firefox # 运行一个无法访问网络的程序 firejail --net=none /usr/bin/myapp
特殊的文件系统挂载选项
在挂载文件系统或目录时,可以使用 noexec、nosuid、nodev 选项来限制可执行文件的运行。
-
场景: 挂载
/tmp目录或上传目录时。# 在 /etc/fstab 中 /dev/sdaX /tmp ext4 defaults,nosuid,noexec,nodev 0 0 # 或使用 mount 命令(临时) sudo mount -o remount,nosuid,noexec,nodev /tmp
noexec:禁止从此文件系统执行任何二进制文件或脚本。nosuid:忽略此文件系统上的 SUID/SGID 位。nodev:不解释此文件系统上的块设备文件。
一份严格限制执行权限的最佳实践清单
- 绝不使用 root 运行非必要服务。
- 为每个服务创建独立、无 shell 的系统用户。
- 严格文件权限:
chmod 750或640,避免 777 和 755,精确控制写权限。 - 用 Capabilities 替代危险的 SUID 脚本: 只授予程序完成任务所需的最小能力。
- 启用 Seccomp: 在容器或通过工具(如
nsjail)限制系统调用。 - 使用容器隔离: 运行
--read-only、--cap-drop=ALL、--security-opt seccomp=...。 - 对
/tmp、上传目录等可写目录挂载noexec。 - 定期审计: 使用
ps aux检查进程运行用户,使用find / -perm /4000查找 SUID 文件,使用getcap -r /查找设置了 Capabilities 的文件。
通过结合以上策略,你可以将攻击面缩小到最小,即使某个服务被攻破,攻击者也无法轻松地横向移动或提权,从而获得整个系统的控制权。