一、引言
在Linux系统中,用户账户管理是系统安全与功能实现的核心基础。作为类Unix操作系统的典型代表,Linux通过一套精密的文件系统架构来实现用户身份识别、权限控制和资源管理。在这一架构中,/etc/passwd和/etc/shadow两个文件扮演着至关重要的角色,它们共同构成了Linux用户账户管理的基础框架。
/etc/passwd文件作为Linux系统中最基本的用户信息存储文件,包含了系统中所有用户的基本信息,如用户名、用户标识号(UID)、组标识号(GID)、用户主目录和默认Shell等。该文件的历史可以追溯到早期的Unix系统,是系统识别用户身份的第一道关口。然而,出于安全考虑,现代Linux系统已将敏感的密码信息从/etc/passwd文件中分离出来,转而存储在/etc/shadow文件中。
/etc/shadow文件,又称"影子文件",专门用于存储用户的密码信息及相关安全策略,包括加密密码、密码最后修改日期、密码有效期、密码过期警告天数等。该文件具有严格的权限控制,通常只有root用户可以读取,有效保护了用户密码的安全性。
这两个文件通过用户名字段建立关联关系,协同工作完成用户账户的创建、认证、管理和维护等全生命周期操作。当用户登录系统时,系统首先查阅/etc/passwd文件确认用户存在,然后通过/etc/shadow文件验证密码;当管理员创建新用户时,系统会同时在两个文件中创建相应的记录;当用户密码被修改时,系统只更新/etc/shadow文件中的密码字段,而/etc/passwd文件保持不变。
深入理解这两个文件的结构、功能及其协同工作机制,对于Linux系统管理员、安全工程师和相关技术人员而言至关重要。这不仅有助于更好地管理系统用户,提高系统安全性,还能在出现相关问题时快速定位和解决。本文将详细解析/etc/passwd和/etc/shadow文件的结构、字段含义、协同工作机制以及相关的安全机制,为读者提供全面而深入的技术指导。
二、/etc/passwd文件结构解析
/etc/passwd文件是Linux系统中存储用户账户信息的基础配置文件,它记录了系统中所有用户的基本信息。该文件中的每一行都代表一个用户账户,并由七个字段组成,字段之间用冒号(:)分隔,格式为:username:password:UID:GID:GECOS:home_directory:shell。下面将详细解析这七个字段的技术含义和系统作用。
| 字段序号 |
字段名称 |
含义描述 |
示例 |
| 1 |
username |
用户登录名,用于唯一标识用户账户 |
root |
| 2 |
password |
密码占位符,现代系统中通常为"x" |
x |
| 3 |
UID |
用户标识号,系统内部用于识别用户的数字ID |
0 |
| 4 |
GID |
组标识号,用户所属主要组的数字ID |
0 |
| 5 |
GECOS |
用户注释信息,可包含全名、电话等 |
root |
| 6 |
home_directory |
用户主目录路径 |
/root |
| 7 |
shell |
用户登录后使用的默认Shell程序 |
/bin/bash |
(一)用户名字段(username)
用户名字段是/etc/passwd文件中的第一个字段,也是用户在系统中的登录标识。这个字段存储了用户的登录名,是用户身份的唯一标识符,供用户在登录系统时使用。虽然Linux系统实际上是通过UID来识别用户身份,而不是用户名,但用户名作为人类可读的标识,在系统管理和用户交互中起着重要作用。
用户名在系统中必须具有唯一性,不允许重复。在Linux系统中,用户名的命名通常遵循一定的规范:一般以字母开头,可以包含字母、数字、下划线、连字符等字符,但不建议使用特殊字符。用户名的长度通常限制在32个字符以内,具体取决于系统配置。例如,在典型的Linux系统中,我们可以看到如"root"、"bin"、"daemon"、"sys"等系统用户,以及普通用户如"john"、"mary"等。
用户名字段的重要性体现在多个方面:首先,它是用户登录系统的凭证;其次,它是系统识别用户身份的第一步;再次,它是系统管理员管理用户账户的基础。当用户尝试登录系统时,系统首先会检查/etc/passwd文件中是否存在该用户名,如果存在,则继续验证密码和其他信息;如果不存在,则拒绝登录请求。
在实际应用中,用户名字段的内容会直接显示在系统登录提示符、命令行提示符以及各种系统管理工具中。例如,当用户成功登录后,系统通常会显示类似"Last login: Tue May 24 08:30:15 2026 from 192.168.1.100"的信息,其中就包含了用户名信息。此外,系统中的许多命令和工具,如who、w、last等,都会显示当前或历史登录的用户名信息。
(二)密码字段(password)
密码字段是/etc/passwd文件中的第二个字段,在现代Linux系统中,这个字段通常包含一个"x"字符,而不是实际的密码。这种设计是出于安全考虑的结果,将实际的密码信息从对所有用户可读的/etc/passwd文件中分离出来,存储在权限更严格的/etc/shadow文件中。
从历史上看,这个字段曾经直接存储加密的用户密码。在早期的Unix系统中,/etc/passwd文件确实包含了加密后的密码,因为当时系统用户数量较少,且多用于学术研究环境,安全威胁相对较小。然而,随着系统规模的扩大和网络攻击的增加,这种设计暴露出严重的安全隐患:由于/etc/passwd文件对所有用户可读,恶意用户可以获取加密密码并尝试离线暴力破解。
为了解决这一安全问题,现代Linux系统采用了"影子密码"机制:在/etc/passwd文件的密码字段中只存放一个"x"占位符,表示实际密码存储在/etc/shadow文件中。这种分离设计大大提高了系统的安全性,因为/etc/shadow文件只有root用户可以读取,普通用户无法获取密码信息。
需要注意的是,虽然密码字段中的"x"并不代表真正的密码,但也不能随意删除或修改。如果删除了这个"x",系统会认为这个用户没有密码,可能导致只输入用户名而不用输入密码就可以登录(仅适用于无密码登录,不适用于远程登录)。同样,如果将此字段设置为"*"或其他特殊字符,可能会导致账户被锁定或禁用。
在实际系统中,我们可以通过查看/etc/passwd文件来确认这一设计。例如,典型的用户记录可能如下所示:
root:x:0:0:root:/root:/bin/bashjohn:x:1000:1000:John Smith:/home/john:/bin/bash
其中,第二个字段都是"x",表示密码存储在/etc/shadow文件中。
(三)用户标识号字段(UID)
用户标识号(User ID,简称UID)是/etc/passwd文件中的第三个字段,这是一个分配给每个用户的唯一数值。Linux系统内部实际上是通过UID来识别用户身份,而不是用户名。UID在系统权限管理、进程控制、文件访问等方面扮演着核心角色。
在Linux系统中,UID的分配遵循一定的规则和范围:
| UID范围 |
用户类型 |
说明 |
| 0 |
超级用户(root) |
拥有系统最高权限,可以执行任何操作 |
| 1-499(或1-999) |
系统用户 |
用于运行系统服务和进程,通常不允许登录 |
| 500(或1000)及以上 |
普通用户 |
正常的系统用户,可以登录和使用系统 |
UID为0的用户是超级用户(root),拥有系统的最高权限。root用户可以执行任何操作,包括修改系统配置、安装软件、管理用户账户等。由于root权限过大,在日常操作中应尽量避免使用root账户,以防止误操作导致系统问题。
UID为1-499(在某些系统中是1-999)的范围保留给系统用户。这些用户通常用于运行系统服务和后台进程,如bin、daemon、sys、lp等。系统用户通常不允许登录系统,其Shell字段通常设置为/sbin/nologin或/bin/false,以防止交互式登录。
UID为500(在某些系统中是1000)及以上的范围用于普通用户。这些是正常使用系统的用户,可以登录系统、运行程序、访问文件等。每个普通用户都有一个唯一的UID,系统通过UID来识别和管理用户。
UID的重要性体现在多个方面:首先,它是系统内部识别用户的唯一标识;其次,它决定了用户对系统资源的访问权限;再次,它影响了进程的执行权限。例如,当用户创建文件时,文件的所有者ID就是该用户的UID;当用户运行程序时,进程的有效UID决定了进程可以访问的资源。
在实际应用中,可以通过id命令查看当前用户的UID信息,也可以通过/etc/passwd文件查看所有用户的UID分配情况。例如:
$ id -u1000$ cat /etc/passwd | grep johnjohn:x:1000:1000:John Smith:/home/john:/bin/bash
这些命令显示了用户john的UID为1000,属于普通用户范围。
(四)组标识号字段(GID)
组标识号(Group ID,简称GID)是/etc/passwd文件中的第四个字段,这是一个唯一的数值,代表用户所属的主要组。该组可以在/etc/group文件中找到对应的详细信息。每个用户都有一个初始组(也称为主要组),通常与用户名相同。用户还可以加入其他多个用户组作为附加组,但GID字段仅指代用户的初始组。
GID的分配规则与UID类似,也遵循一定的范围划分:
| GID范围 |
组类型 |
说明 |
| 0 |
root组 |
与root用户对应,拥有系统最高权限 |
| 1-499(或1-999) |
系统组 |
用于系统服务和进程,通常与系统用户对应 |
| 500(或1000)及以上 |
普通组 |
与普通用户对应,用于文件权限管理 |
GID在Linux系统中的主要作用是管理文件和目录的访问权限。在Linux系统中,每个文件和目录都有三组权限:所有者权限、组权限和其他用户权限。当用户创建文件时,文件的所属组就是该用户的主要组(由GID指定)。组权限决定了该组成员对文件的访问权限,如读、写、执行等。
例如,当用户john(UID为1000,GID为1000)创建一个文件时,该文件的所有者是john,所属组是john(假设GID为1000对应john组)。如果john组的权限设置为rw-,那么john组的所有成员都可以读取和修改该文件。
用户可以属于多个组,但只有一个主要组。主要组在用户创建文件时默认作为文件的所属组,而附加组则用于授予用户对特定资源的访问权限。例如,用户john的主要组可能是john(GID为1000),但他也可能是users组(GID为100)和developers组(GID为1001)的成员,这样他就可以访问这些组所拥有的资源。
在实际应用中,可以通过以下命令查看用户的组信息:
$ id johnuid=1000(john) gid=1000(john) groups=1000(john),100(users),1001(developers)
这个命令显示了用户john的UID为1000,主要组GID为1000(john组),同时还属于users组(GID为100)和developers组(GID为1001)。
(五)GECOS字段(注释字段)
GECOS字段是/etc/passwd文件中的第五个字段,通常用于存储与用户相关的附加信息,如用户的全名、电话号码、部门、房间号等。这个字段可以为空,也可以包含多个用逗号分隔的信息项。GECOS这个名字是旧操作系统的历史遗迹,来源于General Electric Comprehensive Operating Supervisor(GECOS)系统,早期的Unix系统曾用该字段存储GECOS系统的作业信息。
在现代Linux系统中,GECOS字段通常包含以下信息(按顺序):
- 用户全名或真实姓名
- 房间号或工作地点
- 工作电话号码
- 家庭电话号码
- 其他信息
这些信息项之间用逗号分隔,例如:
john:x:1000:1000:John Smith,Room 101,555-1234,555-5678:/home/john:/bin/bash
在这个例子中,GECOS字段包含了用户john的全名(John Smith)、房间号(Room 101)、工作电话(555-1234)和家庭电话(555-5678)。
GECOS字段的主要作用是为系统管理员和用户提供额外的用户信息。虽然这些信息不直接影响系统的功能和权限,但在用户管理和系统维护中非常有用。例如,系统中的finger命令可以显示GECOS字段中的信息,帮助识别用户;邮件系统可以使用GECOS字段中的全名来生成更友好的邮件地址;系统管理工具可以利用这些信息生成用户报告。
在实际应用中,GECOS字段通常在创建用户时通过useradd命令的-c选项设置,也可以 later通过usermod命令的-c选项修改。例如:
$ sudo useradd -c "John Smith,Room 101,555-1234" john$ sudo usermod -c "John Smith,Room 102,555-4321" john
这些命令分别创建和修改了用户john的GECOS字段信息。
需要注意的是,GECOS字段中的信息通常是可选的,许多系统用户(如系统服务用户)的GECOS字段为空或只包含简单的描述。对于普通用户,建议至少设置用户全名,以便于系统管理和用户识别。
(六)主目录字段(home_directory)
主目录字段是/etc/passwd文件中的第六个字段,它指定了用户主目录的路径。用户主目录是用户登录系统后的默认工作目录,也是用户存储个人文件的主要位置。每个用户都应该有一个独立的主目录,用于存放个人配置文件、文档、数据等。
在Linux系统中,主目录的命名通常遵循一定的规则:root用户的主目录是/root,普通用户的主目录通常在/home/用户名下。例如:
root:x:0:0:root:/root:/bin/bashjohn:x:1000:1000:John Smith:/home/john:/bin/bash
在这个例子中,root用户的主目录是/root,而普通用户john的主目录是/home/john。
主目录在系统中的重要性体现在多个方面:首先,它是用户个人文件的存储位置;其次,它包含了用户的个人配置文件,这些文件决定了用户的环境设置和应用程序行为;再次,它是用户登录后的默认工作位置,用户可以方便地访问自己的文件。
当用户登录系统时,系统会自动将用户切换到其主目录。用户可以通过~符号快速引用自己的主目录,例如cd ~命令会将用户切换到其主目录。用户也可以通过~username符号引用其他用户的主目录,例如cd ~john会切换到用户john的主目录(前提是有适当权限)。
主目录通常包含一些隐藏的配置文件(以.开头),这些文件决定了用户的环境设置。例如,.bashrc文件包含了Bash Shell的配置,.profile文件包含了用户登录时执行的命令,.ssh目录包含了SSH密钥和配置等。这些配置文件使得每个用户可以拥有个性化的系统环境。
在实际应用中,主目录通常在创建用户时通过useradd命令的-m和-d选项创建和指定。例如:
$ sudo useradd -m -d /home/john john
这个命令会创建用户john,并在/home目录下创建john的主目录。如果需要修改用户的主目录,可以使用usermod命令的-d选项:
$ sudo usermod -d /home/new_john john
这个命令会将用户john的主目录修改为/home/new_john。
需要注意的是,主目录的权限设置也很重要。通常,主目录应该只有用户本人有写权限,其他用户没有写权限,以防止其他用户修改或删除用户的文件。可以通过以下命令设置适当的权限:
$ sudo chmod 750 /home/john$ sudo chown john:john /home/john
这些命令将用户john的主目录权限设置为750(用户本人可读写执行,组用户可读执行,其他用户无权限),并将所有者和组设置为john。
(七)登录Shell字段(shell)
登录Shell字段是/etc/passwd文件中的第七个字段,它指定了用户登录系统后所使用的默认命令解释器程序。Shell是用户与系统交互的接口,它解释和执行用户输入的命令,提供命令行环境。不同的Shell提供不同的功能和特性,影响用户的工作效率和体验。
在Linux系统中,常见的Shell类型包括:
| Shell类型 |
路径 |
特点描述 |
| Bash |
/bin/bash |
最常用的Shell,功能强大,兼容性好 |
| Sh |
/bin/sh |
传统的Unix Shell,功能较简单 |
| Zsh |
/bin/zsh |
功能丰富的Shell,具有高级特性 |
| Csh |
/bin/csh |
C风格语法,类似C语言 |
| Tcsh |
/bin/tcsh |
Csh的增强版,提供更多功能 |
| Nologin |
/sbin/nologin |
禁止用户登录的Shell |
| False |
/bin/false |
禁止用户登录的Shell |
Bash(Bourne Again Shell)是Linux系统中最常用的Shell,它是Bourne Shell的增强版,提供了丰富的功能,如命令历史、命令补全、作业控制等。Bash具有良好的兼容性,大多数Shell脚本都可以在Bash中运行。因此,大多数Linux发行版将Bash作为用户的默认Shell。
Sh(Bourne Shell)是传统的Unix Shell,功能相对简单,但兼容性好。许多系统脚本使用Sh语法,以确保在不同系统上的兼容性。在现代Linux系统中,/bin/sh通常是Bash的符号链接,但以兼容模式运行。
Zsh(Z Shell)是一个功能丰富的Shell,提供了许多高级特性,如更强大的命令补全、主题支持、插件系统等。Zsh受到许多高级用户的喜爱,但不是所有系统都默认安装。
Csh(C Shell)和Tcsh(TENEX C Shell)使用类似C语言的语法,与Bourne系列的Shell(如Bash、Sh)语法不同。这些Shell在某些特定领域(如Berkeley Unix)比较流行,但在Linux系统中使用较少。
Nologin和False是特殊的Shell,用于禁止用户登录系统。当用户的Shell字段设置为/sbin/nologin或/bin/false时,该用户无法以交互方式登录系统,只能用于系统服务或其他特定用途。这对于系统用户(如daemon、bin等)非常有用,可以防止这些用户被用于交互式登录。
在实际应用中,可以通过以下命令查看系统可用的Shell:
$ cat /etc/shells/bin/sh/bin/bash/usr/bin/sh/usr/bin/bash/bin/tcsh/bin/csh
这个命令列出了系统中所有可用的Shell。用户可以通过chsh命令更改自己的默认Shell:
$ chsh -s /bin/zsh
这个命令将当前用户的默认Shell更改为Zsh。系统管理员也可以通过usermod命令更改其他用户的Shell:
$ sudo usermod -s /bin/zsh john
这个命令将用户john的默认Shell更改为Zsh。
需要注意的是,Shell字段的设置会影响用户的登录体验和工作方式。选择合适的Shell可以提高用户的工作效率和舒适度。同时,对于系统用户,应该将其Shell设置为/sbin/nologin或/bin/false,以防止安全风险。
三、/etc/shadow文件结构解析
/etc/shadow文件是Linux系统中用于存储用户密码信息的关键文件,又称为"影子文件"。该文件只有root用户拥有读权限,默认权限为600,最小权限为400,这样可以有效保证密码的安全性。如果该文件权限发生改变,需要注意是否是恶意攻击的结果。/etc/shadow文件中每行代表一个用户,使用冒号(:)作为分隔符,每行用户信息被划分为9个字段。下面将详细解析这9个字段的技术含义和系统作用。
| 字段序号 |
字段名称 |
含义描述 |
示例 |
| 1 |
用户名 |
与/etc/passwd中的用户名相对应 |
root |
| 2 |
加密密码 |
存储经过加密处理的用户密码 |
$6$longsalt$... |
| 3 |
密码最后修改日期 |
从1970年1月1日开始的天数 |
19215 |
| 4 |
密码最小使用间隔 |
两次密码修改的最小间隔天数 |
0 |
| 5 |
密码有效期 |
密码必须更改的最大天数 |
99999 |
| 6 |
密码修改警告天数 |
密码过期前的警告天数 |
7 |
| 7 |
密码过期后宽限时间 |
密码过期后的宽限天数 |
0 |
| 8 |
账号失效时间 |
账号失效的日期(天数) |
空 |
| 9 |
保留字段 |
预留字段,目前未使用 |
空 |
(一)用户名字段
/etc/shadow文件中的第一个字段是用户名,它与/etc/passwd文件中的用户名相对应,用于标识用户的身份。这个字段是两个文件之间的关键连接点,系统通过用户名将/etc/passwd中的基本信息与/etc/shadow中的密码信息关联起来。
用户名字段在/etc/shadow文件中的重要性体现在多个方面:首先,它是两个文件之间的桥梁,确保用户信息的一致性;其次,它是系统查找用户密码信息的索引;再次,它是用户账户管理的基础。当系统需要验证用户身份时,首先通过用户名在/etc/passwd文件中查找用户基本信息,然后通过相同的用户名在/etc/shadow文件中查找对应的密码信息。
在实际系统中,/etc/shadow文件中的用户名必须与/etc/passwd文件中的用户名完全一致,包括大小写。这种一致性是通过系统命令(如useradd、usermod、userdel)自动维护的,管理员不应该手动编辑这两个文件来保持一致性,以免造成错误。
例如,在典型的Linux系统中,/etc/passwd和/etc/shadow文件中对应的用户记录可能如下:
/etc/passwd:
john:x:1000:1000:John Smith:/home/john:/bin/bash
/etc/shadow:
john:$6$longsalt$encryptedpassword:19215:0:99999:7:::
在这个例子中,两个文件中的用户名都是"john",系统通过这个用户名将两个文件中的记录关联起来。
需要注意的是,/etc/shadow文件中的用户名字段虽然与/etc/passwd文件中的用户名字段相对应,但它的作用仅限于作为连接点。用户的其他基本信息(如UID、GID、主目录等)仍然存储在/etc/passwd文件中,而密码相关信息则存储在/etc/shadow文件中。这种分离设计既保证了用户信息的完整性,又提高了系统的安全性。
(二)加密密码字段
加密密码字段是/etc/shadow文件中的第二个字段,这里保存的是经过加密处理的用户密码。目前Linux系统通常使用SHA-512散列加密算法对密码进行加密,这种加密算法的等级更高,也更加安全。如果这个字段显示为两个感叹号(!!)或一个星号(*),表示该用户没有设置密码,无法登录。如果这个字段是x,则表示这个用户设有密码,真正的密码保存在shadow文件中。这个密码是单向不可逆的,不能手工修改,否则系统将无法识别密码,导致密码失效。
在现代Linux系统中,密码字段的格式通常遵循Modular Crypt Format(MCF),其基本结构为:
$id$salt$encrypted
其中:
- $id$ 表示使用的加密算法ID
- salt 是随机生成的字符串,用于增加密码强度
- encrypted 是经过加密后的密码散列值
常见的加密算法ID包括:
| 算法ID |
加密算法 |
描述 |
| $1$ |
MD5 |
较早的加密算法,现已不推荐使用 |
| $2a$ 或 $2y$ |
bcrypt |
基于Blowfish的加密算法,安全性较高 |
| $5$ |
SHA-256 |
SHA-2家族的256位加密算法 |
| $6$ |
SHA-512 |
SHA-2家族的512位加密算法,目前最常用 |
例如,一个使用SHA-512算法加密的密码可能如下所示:
$6$longsaltstring$encryptedpasswordhashstring
在这个例子中,$6$表示使用SHA-512算法,longsaltstring是盐值,encryptedpasswordhashstring是加密后的密码散列值。
盐值(salt)是一个随机生成的字符串,它与用户密码一起用于生成加密后的密码散列值。通过使用盐值,可以确保即使两个用户的密码相同,它们在/etc/shadow文件中存储的加密密码也会不同,有效防止彩虹表攻击等暴力破解方法。盐值通常足够长(8-16个字符),包含字母、数字和特殊字符,以增加密码的复杂性。
当用户登录系统时,系统会获取用户输入的密码,与存储的盐值结合,使用相同的加密算法生成新的散列值,然后将这个散列值与存储的散列值进行比较。如果两者匹配,则密码验证成功;否则,验证失败。这种单向加密机制确保了即使攻击者获取了/etc/shadow文件,也无法直接还原出用户的原始密码。
需要注意的是,密码字段的特殊值有特定含义:
- !! 或 *:表示账户被锁定或密码未设置,用户无法通过密码登录

2万+

被折叠的 条评论
为什么被折叠?



