尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux特殊权限位SUID、SGID、Sticky实战配置与避坑指南

Linux特殊权限位SUID、SGID、Sticky实战配置与避坑指南 在Linux里敲了这么多年命令权限相关的坑几乎每个运维都踩过明明文件权限设成了755普通用户却能把系统密码改了团队共享目录里同事新建的文件隔天就变成别人的私有/tmp里自己的临时文件莫名其妙被同名覆盖。这些看似灵异的现象根子大多不在常规的rwx九位权限上而在于三位容易被忽略的隐藏角色——suid、sgid、sticky。它们是Linux特殊权限位里最核心的三个开关也是面试里高频出现、线上却经常被配错的知识点。这篇不讲教科书定义我从实战角度把这三位的来历、生效条件、配置方法、踩坑记录一次讲透不管你是刚学linux命令的新手还是天天和服务器打交道的运维老手看完都能直接上手排查和配置。1. 权限模型回顾与三个特殊位的来龙去脉1.1 从九位rwx到看不见的第十二位我们平时用ls -l看到的权限字符串比如-rwxr-xr-x是九位。拆开看分别是属主user、属组group、其他人other三组每组三位读r、写w、执行x。这套九位模型管的是谁能读、谁能写、谁能执行是Linux权限体系的地基。但九位模型有三个它表达不了的需求。第一普通用户要修改自己的密码而密码文件归root所有九位权限没法让普通用户临时借用root身份。第二团队共享目录里希望新建的文件自动继承父目录的属组九位权限只管属主管不了属组继承。第三像/tmp这种所有人都能写的目录需要防止用户删掉别人的文件九位权限里谁有写权限谁就能删没有例外。Linux给出的答案是在九位之外再挂三位。它们单独存在和rwx叠加共同构成一个最多十二位的权限空间。这三位的八进制权重分别是SUID等于4SGID等于2Sticky等于1。这就是为什么你会看到4755、2775、1777这种四位数权限——第一位就是特殊位的开关。1.2 三个特殊位各自要解决什么问题用一句人话概括SUIDSet User ID程序运行时临时借用文件属主的身份。SGIDSet Group ID程序运行时临时借用文件属组的身份作用在目录上时让新建文件继承目录的属组。Sticky作用在目录上让目录里的文件只有属主或root才能删除和改名。打个生活化的比方。九位权限像是小区门禁卡规定了谁能进哪个门。而SUID像是临时工牌——你去办理一项业务柜台给你一张临时的、只在这一件事上有效的身份凭证。SGID在目录上的作用像是统一的签名章谁往这个目录里放文件都会自动盖上这个部门的章。Sticky则像是合租房的储物柜规则——公共区域谁都能放东西但只有你自己的柜子你能清空。这三个位的设计初衷都是为了安全和协作但一旦配错反而会变成最大的安全缺口。这也是为什么它们是安全审计的重点对象。1.3 八进制与符号两种表示法配置特殊位有两套写法必须都掌握否则看到别人给的命令会发懵。符号法最直观chmod us file # 给文件加SUID chmod gs dir # 给目录加SGID chmod t dir # 给目录加Sticky chmod u-s file # 取消SUID八进制法则是在原有三位权限前加一位数字chmod 4755 file # SUID rwxr-xr-x chmod 2775 dir # SGID rwxrwxr-x chmod 1777 dir # Sticky rwxrwxrwx注意八进制里把特殊位和普通权限写在一起时一定是四位数。如果你写chmod 755那不是特殊位为0的755而是直接清掉了特殊位。很多新手以为chmod 2755和chmod 755只差一位实际差的是整个SGID开关。符号法和八进制的对应关系是4SUID2SGID1Sticky相加得到第一位。比如同时要SUID和SGID就是6开头的6755这在一些系统工具上确实存在。2. SUID让普通用户临时借用属主身份2.1 工作机制exec的那一刻发生了什么SUID的全称是Set User ID设置在一个可执行文件上。当设置了SUID的程序被执行时进程的有效用户IDEUID会从执行者的UID变成文件属主的UID。注意这里是有效UID不是真实UIDRUID。真实UID仍然是执行者本人这在审计和日志里很重要。这里的关键词是进程。SUID改变的是运行中进程的身份而不是改变文件本身的权限也不是改变执行者的账户。程序一退出这个临时身份就没了。很多人误以为SUID是给用户发了个root权限其实它是给这一次执行的进程发了一张临时通行证。理解这一点就能解释很多现象。比如一个SUID程序被fork出子进程子进程默认会继承这个有效UID再比如程序内部如果调用了setuid()主动降权那就真的回到普通用户了。这也是很多守护进程启动后主动放弃特权的原理。2.2 passwd命令为什么必须靠它最经典的SUID例子就是修改密码。密码的哈希存在/etc/shadow里这个文件的权限通常是----------或000属主root连组和其他人都没有任何权限。按理说普通用户根本碰不了它。但每个用户都得能改自己的密码。怎么办做法是/usr/bin/passwd这个程序本身被设置成SUID root。ls -l /usr/bin/passwd -rwsr-xr-x. 1 root root 27832 Jun 10 2014 /usr/bin/passwd注意属主权限那一组里的s它取代了原来的x位置。普通用户执行passwd时这个进程就临时拥有了root的有效UID于是它就能去写/etc/shadow。但程序内部做了严格限制——只允许你改自己的密码root的密码还得先验证旧密码。所以SUID并没有把root权限泄露给用户只是精确地授权了改密码这一件事。这就是SUID的正确用法把需要特权的单一功能封装成一个程序只开放它该做的事。2.3 SUID生效的硬性条件SUID不是设了就一定生效有几个硬条件第一作用对象必须是一个二进制可执行文件或本质可执行的程序。你给一个文本文件、图片、目录设置SUID它不会按你想的方式工作。给目录设SUID在Linux上基本没有意义会被忽略。第二文件必须真的有执行位至少要给对应角色x权限。如果属主的x位没有你用符号法设置SUID后ls -l会显示一个大写的S而不是小写s。大写S表示SUID设了但执行位没开实际不生效。这是个非常经典的坑下面单独讲。第三文件系统不能禁用SUID。有些挂载选项比如nosuid会直接让整个分区上的SUID失效。云服务器数据盘、容器挂载卷经常带nosuid在那种分区上折腾半天也不生效。用mount | grep nosuid可以快速排查。第四现代内核对脚本的SUID基本不认。过去给shell脚本设SUID还能凑合跑现在主流发行版出于安全考虑直接忽略脚本上的SUID。所以别想着写个bash脚本加SUID来搞特权大概率无效而且这本身就是危险写法。2.4 设置、取消与验证实操给你一套可以直接抄的完整流程。先造个测试环境# 用root创建一个简单的C程序 cat /tmp/hello.c EOF #include stdio.h #include unistd.h int main() { printf(RUID%d EUID%d\n, getuid(), geteuid()); return 0; } EOF gcc -o /tmp/hello /tmp/hello.c chmod 755 /tmp/hello正常执行时RUID和EUID都是当前用户。现在加上SUIDchown root:root /tmp/hello chmod us /tmp/hello ls -l /tmp/hello # 输出类似-rwsr-xr-x. 1 root root ... /tmp/hello再用普通用户执行/tmp/hello # RUID1000 EUID0你看真实用户还是1000但有效用户变成了0root。这就是SUID生效的铁证。取消SUIDchmod u-s /tmp/hello # 或者用八进制chmod 755 /tmp/hello提示生产环境上排查哪些文件带SUID用这条命令find / -perm -4000 -type f 2/dev/null-perm -4000表示至少包含SUID位。加-type f只看普通文件避免把设备节点也算进来。这条命令建议每个运维都背下来安全巡检时必用。2.5 大写S的坑与安全审计思路前面提到的小写s和大写S是新手最容易翻车的地方。规律很简单小写sSUID开了执行位也开了真正生效。大写SSUID开了但执行位没开等于白设。chmod 4644 testfile ls -l testfile # -rwSr--r-- ← 大写S不生效 chmod 4744 testfile ls -l testfile # -rwsr--r-- ← 小写s但只有属主能执行排查时如果发现权限位里是大写S第一反应就是去补执行位。再讲讲安全审计。SUID是一把双刃剑历史上不少本地安全事件都和配错的SUID程序有关。常见风险场景包括给交互式解释器python、perl、bash设了SUID等于给了任何人一个root shell给编辑器、打包工具设SUID它们能顺手读写任意文件。运维的正当防御动作是定期盘点检查项命令关注点全盘SUID文件find / -perm -4000 -type f 2/dev/null是否有非预期文件全盘SGID文件find / -perm -2000 -type f 2/dev/null同上分区的nosuid选项mount | grep nosuid判断SUID是否被禁用文件属主变化find / -perm -4000 -newer /etc/passwd近期新增的SUID注意如果你在服务器上发现了自己完全不认识的SUID可执行文件不要急着删先记录路径、属主、时间戳、哈希值结合业务确认来源。贸然删除可能影响正常服务也可能破坏排查线索。3. SGID它有两副面孔别搞混3.1 作用在文件上借用属组身份SGID作用在可执行文件上时逻辑和SUID对称。程序执行时进程的有效组IDEGID会变成文件属组的GID并补充进进程的附加组列表。真实组ID不变。它和SUID一样是为了解决普通用户需要某个组的特权来完成一件事的问题。表现形式是属组权限位上的sls -l somefile # -rwxr-sr-x ← 属组位置的sSGID生效同样有大小写问题小写s表示SGID加执行位都在大写S表示SGID设了但执行位缺失不生效。文件上的SGID在实际业务里比SUID用得少多见于一些需要特定组权限的老牌系统工具。真正值钱、运维天天打交道的是下面这一副面孔。3.2 作用在目录上团队协作的组继承当SGID设置在一个目录上时行为完全变了在这个目录里新建的文件、子目录会自动继承该目录的属组而不是继承创建者的主组。同时新建的子目录会自动带上SGID位让继承效果逐层延续下去。这个特性直接解决了一个老大难问题。想象一个团队共享目录成员A、B、C都属于同一个项目组devteam但每个人还有一个私有主组比如alice、bob。如果没有SGIDA在共享目录里建的文件属组会是aliceB可能因为组不对而写不进去协作立刻乱套。设置SGID后不管谁建文件属组统一是devteam配合合理的组权限协作顺畅。这就是为什么几乎所有团队共享目录的标准配法是2775SGID2rwxrwxr-x775。3.3 从零搭一个团队共享目录这套配置我建议每个运维都自己走一遍理解会深很多。# 1. 创建项目组和目录 groupadd devteam mkdir -p /srv/share chown root:devteam /srv/share # 2. 设置SGID保证新建文件继承devteam组 chmod 2775 /srv/share ls -ld /srv/share # drwxrwsr-x 属组位上是sSGID生效 # 3. 把用户加进组以alice为例 usermod -aG devteam alice验证继承效果# 用alice登录后需要重新登录使组生效 touch /srv/share/alice.txt ls -l /srv/share/alice.txt # -rw-rw-r-- 1 alice devteam ... alice.txt # 属组是devteam不是alice自己的主组这里有个高频坑点必须提醒改完用户组后已经登录的会话不会自动更新组成员身份必须让用户重新登录或至少新开一个会话。很多我加了组还是不生效的问题根源就在这。用id命令可以确认当前会话里到底有哪些组。还有一个坑SGID保证了属组继承但没有保证组写权限。新建的文件权限还会受umask影响默认可能没有组写位rw-r--r--。队友之间要能互相改还得配合两点一是把umask设成002保证组内可写二是对已有文件补上组写权限。所以真正完备的共享目录往往还要加一条默认ACL兜底setfacl -d -m g::rwx /srv/share setfacl -d -m o::rx /srv/share默认ACL管的是目录里未来新建文件继承什么权限和SGID的组继承互补是团队协作场景的黄金组合。3.4 设置、取消与验证SGID的符号操作chmod gs /srv/share # 加SGID chmod g-s /srv/share # 取消SGID八进制操作和SUID同理第一位加2chmod 2775 /srv/share排查全盘哪些目录带了SGID用find / -type d -perm -2000 2/dev/null这个列表通常不会太长如果冒出很多不认识的、指向系统目录的SGID目录值得警惕。正常的SGID目录往往是你自己为协作建的或者是某些服务比如邮件、数据库的专用目录。4. Sticky位目录里的防误删保险4.1 它的历史与现在的唯一有效场景Sticky位的历史比前两位更曲折。在早期系统里它作用在可执行文件上能把程序代码粘在内存里不被换出加快再次启动。这个用途早就被现代内存管理淘汰了。今天在Linux上Sticky位只对目录有意义。它对目录的作用是目录里所有用户都能新建文件只要目录有写权限但只有文件的属主、目录的属主或root才能删除、改名、移动这个文件。注意它限制的是删除和改名不是读取和修改内容。这条规则精准命中了共享可写目录的最大痛点谁都能写就意味着谁都能删。Sticky把它们拆开了——能写但删不了别人的。4.2 /tmp的经典设计打开任何一台Linux看看/tmpls -ld /tmp # drwxrwxrwt. 1 root root ... /tmp末尾的t就是Sticky位。/tmp的权限是1777意味着所有人可读可写可执行但因为有Sticky位你删不掉别人的临时文件。这个设计既保证了任何程序都能在/tmp里放东西又避免了用户互相破坏。设想没有Sticky位的后果任何普通用户都能rm /tmp/别的用户正在用的文件一个恶意或者粗心的操作就能搞垮别人的程序。这也是为什么一些安全规范会特别检查1777目录发现没带Sticky的可写全局目录会直接标为风险点。4.3 设置与验证实操# 建目录并设置Sticky mkdir -p /srv/public chmod 1777 /srv/public ls -ld /srv/public # drwxrwxrwt ← 末尾t # 符号写法 chmod t /srv/public chmod -t /srv/public # 取消验证效果# root创建文件 touch /srv/public/root_file # 普通用户alice尝试删除 rm /srv/public/root_file # rm: cannot remove /srv/public/root_file: Operation not permitted # 但alice可以删自己建的 touch /srv/public/alice_file rm /srv/public/alice_file # 成功和大小写S一样Sticky也有大小写问题小写tSticky开了其他人的执行位也开了完整生效。大写TSticky开了但其他人没有执行位效果受限。1777对应小写t1776就会显示大写T。要共享目录正常工作一般是1777。提示如果你要建一个人人可写但不能删别人文件的公共上传目录标准配法就是1777配合属主root。别用0777那等于没防护。5. 三个特殊位对照速查与组合使用5.1 一张表看懂三位特殊位八进制符号作用对象效果典型值SUID4us可执行文件运行时借用属主身份4755SGID2gs文件或目录文件借属组目录继承属组2755 / 2775Sticky1t目录只允许属主删除自己文件1777组合时的第一位就是相加SUIDSGID6SGIDSticky3三位全开7。比如6777、3777都能构成但现实中很少这么用组合越多越难维护风险也越难评估。5.2 一个组合实战共享目录的完整配法团队共享目录推荐2775加默认ACL公共上传目录推荐1777。这两个配法覆盖了绝大多数场景。如果你遇到需要既是团队共享又要防止误删的极端需求可以叠加SGID和Sticky写成3775目录里新建文件继承组同时成员只能删自己的文件。这种组合在协作平台的上传区偶有出现但要清楚告诉使用者行为否则大家会疑惑为什么删不掉别人的文件。chmod 3775 /srv/team_share ls -ld /srv/team_share # drwxrwsr-t5.3 capabilities正在替代一部分SUID值得补充一个现代趋势。SUID把整个root身份借给一个程序粒度太粗风险大。Linux的capabilities机制把root权限拆成几十种细粒度能力比如cap_net_raw只允许发原始网络包cap_net_bind_service只允许绑定1024以下端口程序只需拿到自己需要的那一小块能力。典型的例子是ping。以前ping要SUID root现在很多发行版直接给二进制文件加cap_net_raw能力不再需要SUIDgetcap /usr/bin/ping # /usr/bin/ping cap_net_rawep如果你在设计新程序需要一点特权但又不是全部root优先考虑capabilities而不是图省事设SUID。这是安全加固的正确方向。查看和设置命令是getcap、setcap用法直观。6. 常见问题与排查技巧实录6.1 umask如何悄悄吃掉你的特殊位有个反直觉的现象你用chmod 2775设好SGID过一阵发现新目录没有SGID了。别怀疑人生先看两件事。第一普通用户用mkdir建目录时目录的默认权限里本来就带SGID——准确说mkdir创建的目录本来就继承父目录的SGID设置。但如果你用的是cp -r、unzip、tar -x这些工具它们创建目录的方式各不相同某些会显式指定权限从而覆盖掉继承来的特殊位。第二umask会限制新建文件的权限上限。umask是要屏蔽掉哪些位比如umask 022会去掉组和其他人的写权限。但umask一般只管rwx九位不影响特殊位的继承逻辑。真正的问题在于工具怎么调open/mkdir。排查方法很实在建完目录立刻ls -ld看结果别假设它会自动继承。发现丢了SGID就手动补chmod gs或者在打包脚本里显式设置。6.2 复制和打包时特殊位为什么丢失这是线上最常被投诉的玄学之一。tar打包和cp复制对特殊位的处理规则不同cp默认不太保留特殊位需要加-p保留权限、时间戳、属主或-a归档模式等同于-dR --preserveall才会带上。tar默认会保留权限位但解包时如果是普通用户操作属主无法恢复成原属主特殊位在某些情况下也会被安全策略丢弃。一个稳妥的复制配法是cp -a /srv/share /backup/ # 或 tar czf share.tar.gz /srv/share tar xzf share.tar.gz -C /srv/还原后务必ls -ld复查尤其是SGID和Sticky这两个在共享目录里最容易丢。养成复制完就对比权限的习惯能省掉大量扯皮时间。6.3 常见问题速查表现象可能原因排查命令解决SUID设了但普通用户还是没特权分区带nosuidmount | grep nosuid换分区或改挂载选项权限位显示大写S或T缺少对应执行位ls -l补上x位脚本加SUID无效内核对脚本忽略SUID无改用二进制程序或capabilities共享目录新建文件属组不对未设SGID或用户组未刷新ls -ld 目录、idchmod gs重新登录新文件队友写不了umask限制组写位umask设umask 002或加默认ACL复制后特殊位丢失cp/tar未保留ls -ld用cp -a复制后复查/tmp里删不掉别人文件这是Sticky的正常行为ls -ld /tmp属主或root才能删6.4 我的审计习惯和几条硬经验最后分享几条我踩过坑之后固定下来的习惯都是能直接落地的。第一每次上线新服务后跑一次SUID/SGID盘点把结果和上次比对新增项逐个确认来源。这比事后追查高效得多。find / -perm -4000 -type f 2/dev/null | sort /var/log/suid_baseline.txt第二永远用四位数写特殊权限即使是0也用0755。这样命令一看就知道特殊位有没有被考虑进去避免误清。第三共享目录优先用SGID 默认ACL umask 002三件套比单纯改权限位稳得多尤其在多用户、多工具混用的环境里。第四别把SUID当万能钥匙。能用capabilities、能用sudo精确授权、能用服务拆分解决的就别给程序挂SUID。SUID一个配错可能就是整台机器的安全缺口。第五排查权限问题时先看挂载选项再看权限位。nosuid、noexec这类挂载选项能让你在权限位正确的情况下依然什么都不生效先排除这层再往下查能少走一半弯路。mount | grep -E nosuid|noexec这套组合拳打下来suid、sgid、sticky这三位就不再是玄学开关而是你手里可控、可查、可加固的常规工具。真正理解它们的关键是记住每一层权限背后对应的那个真实需求SUID解决临时借身份SGID解决身份和属组继承Sticky解决共享目录防误删。把需求和机制对上号配置的时候就不会再凭感觉乱填数字了。
返回列表