Unix 系统中安全脚本编写与相关安全机制解析
1. Unix 安全概述
Unix 系统的安全问题声名狼藉,几乎 Unix 系统的每个方面都存在相关的安全隐患,通常系统管理员需要关注这些问题。下面将详细介绍编写安全 shell 脚本的相关内容,包括编写技巧、受限 shell、特洛伊木马以及 setuid shell 脚本等方面。
2. 编写安全 shell 脚本的技巧
为了编写更安全的 shell 脚本,以下是一些由普渡大学信息保障与安全教育研究中心主任 Eugene (Gene) Spafford 教授提供的建议:
1.
避免将当前目录(点)放入 PATH
:可执行程序应仅来自标准系统目录。将当前目录放入 PATH 会为“特洛伊木马”打开大门。
2.
保护 bin 目录
:确保 $PATH 中的每个目录仅可由其所有者写入,bin 目录中的所有程序也应如此。
3.
编码前进行设计
:花些时间思考要做什么以及如何去做,不要只是在文本编辑器中随意输入代码,直到看起来能工作为止。要包含能优雅处理错误和失败的代码。
4.
检查所有输入参数的有效性
:如果期望输入是一个数字,要验证确实得到了一个数字,并检查该数字是否在正确的范围内。对于其他类型的数据也应如此,shell 的模式匹配功能对此特别有用。
5.
检查所有可能返回错误的命令的错误代码
:有些你可能认为不会失败的事情,可能会被恶意强制失败,从而导致脚本出现异常。例如,当参数是 NFS 挂载的磁盘或面向字符的设备文件时,即使以 root 身份执行,某些命令也可能失败。
6.
不要信任传入的环境变量
:如果后续命令使用了这些环境变量(如 TZ、PATH、IFS 等),要检查并将它们重置为已知值。ksh93 在启动时会自动将 IFS 重置为其默认值,忽略环境中的任何设置,但许多其他 shell 不会这样做。在所有情况下,明确将 PATH 设置为仅包含系统 bin 目录,将 IFS 设置为空格、制表符和换行符是个很好的做法。
7.
从已知位置开始
:脚本启动时,明确切换到一个已知的目录,这样后续的相对路径名就指向已知位置。确保 cd 命令成功执行:
cd app-dir || exit 1
- 使用命令的完整路径名
3. 受限 shell
受限 shell 旨在将用户置于一个移动和写入文件的能力受到严重限制的环境中,通常用于来宾账户。POSIX 并未规定环境必须提供受限 shell,因为它无法提供历史文档中所暗示的安全限制级别。不过,ksh93 和 bash 都提供了此功能。
-
ksh93 受限 shell
:当以 rksh 调用(或使用 -r 选项)时,ksh93 作为受限 shell 运行。可以通过在用户的 /etc/passwd 条目中放入 rksh 的完整路径名,使该用户的登录 shell 受限。ksh93 可执行文件必须有一个名为 rksh 的链接才能正常工作。受限 ksh93 对用户有以下限制:
- 无法更改工作目录:使用 cd 命令会失败,并显示错误消息“ksh: cd: restricted”。
- 禁止将输出重定向到文件:不允许使用重定向符号 >、>|、<> 和 >>,包括使用 exec 命令。
- 不能为环境变量 ENV、FPATH、PATH 或 SHELL 分配新值,也不能使用 typeset 命令更改它们的属性。
- 不能指定包含斜杠 (/) 的命令路径名,shell 仅运行 $PATH 中找到的命令。
- 不能使用 builtin 命令添加新的内置命令。
-
bash 受限 shell
:当以 rbash 调用时,bash 作为受限 shell 运行,bash 可执行文件必须有一个名为 rbash 的链接才能正常工作。bash 的受限操作列表与 ksh93 类似:
- 不能使用 cd 命令更改目录。
- 不能设置或取消设置 SHELL、PATH、ENV 或 BASH_ENV 的值。
- 不能指定包含 / 的命令名。
- 不能将包含 / 的文件名作为 .(点)内置命令的参数。
- 不能将包含 / 的文件名作为 hash 内置命令 -p 选项的参数。
- 启动时不能从 shell 环境导入函数定义。
以下是 ksh93 和 bash 受限 shell 限制的对比表格:
| 限制类型 | ksh93 受限 shell | bash 受限 shell |
| ---- | ---- | ---- |
| 更改工作目录 | 禁止 | 禁止 |
| 输出重定向 | 禁止 | 未提及,但类似 |
| 环境变量操作 | 禁止修改特定变量 | 禁止设置或取消设置特定变量 |
| 命令路径名 | 禁止含 / | 禁止含 / |
| 内置命令添加 | 禁止 | 未提及,但类似 |
| 点命令参数 | 未提及 | 禁止含 / |
| hash 命令参数 | 未提及 | 禁止含 / |
| 函数定义导入 | 未提及 | 禁止 |
4. 特洛伊木马
特洛伊木马是一种看似无害甚至有用,但实际上隐藏着危险的程序。以下是一个具体的场景示例:
用户 John Q. Programmer(登录名 jprog)是一位优秀的程序员,他在 ~jprog/bin 目录中有很多个人程序,该目录在 ~jprog/.profile 的 PATH 变量中排在首位。由于他编程能力出色,最近被提升为系统管理员。然而,他没有意识到问题所在,将 bin 目录设置为可被其他用户写入。这时,恶意用户 W.M. Badguy 在 John 的 bin 目录中创建了一个名为 grep 的 shell 脚本:
/bin/grep "$@"
case $(whoami) in # 检查有效用户 ID 名称
root) nasty stuff here # 危险!
rm ~/jprog/bin/grep # 隐藏证据
;;
esac
当 jprog 以自己的身份工作时,这个脚本本身不会造成任何损害。但当 jprog 使用 su 命令切换到 root 用户时,问题就出现了。默认情况下,su 会继承当前的 PATH,而 $PATH 包含 ~jprog/bin。因此,当 jprog 以 root 身份运行 grep 时,实际上执行的是 bin 目录中的特洛伊木马版本。这个版本会运行真正的 grep 命令,让 jprog 得到他期望的结果,但更重要的是,它还会以 root 身份悄悄执行“nasty stuff here”部分,这意味着 Unix 会允许脚本做任何它想做的事情。而且,脚本执行完后会删除自己,不会留下任何证据。
为了避免这种情况,建议养成使用
su - user
命令切换用户的习惯,这样可以防止导入现有的 PATH。可写的 bin 目录、将点放入 PATH 以及 bin 目录中存在可写的 shell 脚本,都会为特洛伊木马打开方便之门,就像晚上要关好并锁好家门一样,也应该确保系统的安全。
5. setuid shell 脚本:一个糟糕的主意
Unix 安全的许多问题都与一个名为 setuid(设置用户 ID)位的文件属性有关。这是一个特殊的权限位,当可执行文件的 setuid 位打开时,该文件将以文件所有者的有效用户 ID 运行。有效用户 ID 与进程的真实用户 ID 不同,Unix 根据进程的有效用户 ID 进行权限检查。
例如,你编写了一个很棒的游戏程序,该程序维护一个私人得分文件,显示系统上的前 15 名玩家。你不希望得分文件被所有人可写,以免有人篡改得分。通过将游戏程序的 setuid 位设置为你的用户 ID,游戏程序可以更新你拥有的得分文件,而其他用户则无法更新。
然而,将程序的 setuid 位设置为 root 是非常危险的。管理员可以使用这种方式编写需要 root 权限才能执行某些操作(如配置打印机)的程序。要设置文件的 setuid 位,可以使用
chmod u+s filename
命令。当 root 拥有该文件时,setuid 会带来危险,例如先使用
chown root file
然后使用
chmod u+s file
就会存在问题。
类似地,在组级别也有 setgid(设置组 ID)功能,使用
chmod g+s filename
可以打开 setgid 权限。当使用
ls -l
查看 setuid 或 setgid 文件时,权限模式中的 x 会被 s 替换,例如 -rws–s–x 表示文件所有者可读可写,所有人可执行,并且同时设置了 setuid 和 setgid 位(八进制模式 6711)。
现代系统管理认为创建 setuid 和 setgid shell 脚本是一个糟糕的主意,尤其是在 C shell 中,因为其 .cshrc 环境文件会引入许多安全漏洞。例如,存在多种方法可以诱使 setuid shell 脚本变成一个具有 root 有效用户 ID 的交互式 shell,这正是黑客梦寐以求的。以下是一个示例:
假设脚本名为 /etc/setuid_script,内容如下:
#!/bin/sh
如果执行以下命令:
$ cd /tmp
$ ln /etc/setuid_script -i
$ PATH=.
$ -i
最后一个命令会被重新排列为
/bin/sh -i
,这将给我们一个具有脚本所有者 setuid 的交互式 shell。幸运的是,通过将第一行改为
#!/bin/sh -
可以轻松关闭这个安全漏洞,因为 - 表示选项列表的结束,下一个参数 -i 将被视为要读取命令的文件名。
由于这个原因,POSIX 明确允许使用单个 - 字符来结束 /bin/sh 的选项。需要注意的是,setuid shell 脚本和 setuid shell 是有重要区别的,后者是 shell 可执行文件的一个副本,该副本属于 root 并设置了 setuid 位。
6. ksh93 的特权模式
Korn shell 的特权模式旨在保护 setuid shell 脚本。这是一个 set -o 选项(set -o privileged 或 set -p),当 shell 执行一个 setuid 位被设置的脚本时,即有效用户 ID 与真实用户 ID 不同时,会自动进入特权模式。
在特权模式下,当调用 setuid Korn shell 脚本时,shell 会运行 /etc/suid_profile 文件。该文件应该像受限 shell 一样,对 setuid shell 脚本进行限制。至少,它应该将 PATH 设置为只读(使用
typeset -r PATH
或
readonly PATH
),并将其设置为一个或多个“安全”目录,这样可以防止调用任何诱饵程序。
虽然特权模式是一个选项,可以使用
set +o privileged
(或
set +p
)命令关闭它,但这对潜在的系统攻击者没有帮助,因为 shell 会自动将其有效用户 ID 更改为与真实用户 ID 相同,即关闭特权模式也会关闭 setuid。
除了特权模式,ksh 还提供了一个特殊的“代理”程序 /etc/suid_exec,用于运行 setuid shell 脚本(或可执行但不可读的 shell 脚本)。为了使这个程序正常工作,脚本不能以
#!/bin/ksh
开头。当调用该程序时,ksh 会尝试将其作为常规二进制可执行文件运行。当操作系统无法运行该脚本时(因为它不是二进制文件,且没有使用 #! 指定解释器),ksh 会意识到这是一个脚本,并使用脚本名及其参数调用 /etc/suid_exec。同时,它会传递一个身份验证“令牌”给 /etc/suid_exec,指示脚本的真实和有效用户及组 ID。/etc/suid_exec 会验证运行脚本是否安全,然后安排以正确的真实和有效用户及组 ID 调用 ksh 来执行脚本。
尽管特权模式和 /etc/suid_exec 的组合可以避免 setuid 脚本的许多攻击,但编写可以安全运行 setuid 的脚本是一项困难的技术,需要相当多的知识和经验,应该谨慎进行。
虽然现代系统通常不允许使用 setuid shell 脚本,但在某些情况下,特权模式仍然很有用。例如,有一个广泛使用的第三方程序 sudo,它允许系统管理员授予某些用户(或用户组)以 root 或其他用户身份运行某些(或所有)命令的能力,同时记录命令和参数。系统管理员可以轻松执行
sudo /bin/ksh -p
以获得一个已知的环境来执行管理任务。
7. 总结
编写安全的 shell 脚本只是保持 Unix 系统安全的一部分,本文只是触及了相关问题的冰山一角。为了更好地保障系统安全,建议进一步了解 Unix 系统安全知识。
编写安全 shell 脚本时,可以遵循前面提到的编写技巧。受限 shell 可以限制用户的操作,但在实际应用中,设置和使用受限 shell 比较困难,可能需要寻找其他方式来设置受限环境。特洛伊木马是一种隐藏危险的程序,要注意避免可写的 bin 目录、将点放入 PATH 等情况。现代 Unix 系统通常不允许使用 setuid shell 脚本,因为很难关闭它们带来的安全漏洞,建议定期检查系统中是否存在此类文件。最后,Korn shell 的特权模式试图解决与 shell 脚本相关的许多安全问题。
8. 编写手册页
程序的用户和作者都需要文档,然而大多数计算机书籍都忽略了软件文档的编写,很多想为程序编写好文档的用户往往不知道如何开始。在 Unix 系统中,简短的编程文档传统上以手册页的形式提供,使用 nroff/troff 标记编写,可以使用 man、nroff -man 或 groff -man 以简单的 ASCII 文本形式显示,使用 ditroff -man -Txxx、groff -man -Txxx 或 troff -man -Txxx 为某些设备进行排版,或者使用 groff -TX -man 在 X 窗口中以排版形式查看。
较长的软件文档通常以手册或技术报告的形式提供,通常使用 troff 标记,打印页面为 PostScript 或 PDF 格式。然而,troff 标记不太友好,因此 GNU 项目选择了另一种方法:Texinfo 文档系统。Texinfo 标记比常见的 troff 包更高级,和 troff 一样,它允许文档既可以作为简单的 ASCII 文本查看,也可以由 TEX 排版系统进行排版。最重要的是,它支持超文本链接,方便在在线文档中进行导航。
大多数在 Unix 系统中在线阅读的文档可能使用了 troff 或 Texinfo 标记。Texinfo 系统的 makeinfo 程序可以生成 ASCII、HTML、XML 和 DocBook/XML 格式的输出。Texinfo 文件可以直接由 TEX 排版,输出一个与设备无关的(DVI)文件,然后可以通过称为 DVI 驱动程序的后端程序将其转换为各种设备格式。
除了 troff 和 Texinfo 标记,还有其他标记格式。例如,从 Solaris 7 开始,Sun Microsystems 几乎所有的手册页都以 SGML 形式提供,Linux 文档项目推广使用 XML(SGML 的子集)标记,以方便将 GNU/Linux 文档翻译成多种人类语言。
对于 Unix 程序作者来说,选择哪种标记系统呢?经验表明,高级标记虽然可能更冗长,但具有很大的价值。SGML(以及 HTML 和 XML)基于严格的语法,因此可以在将文档编译成可显示页面之前验证其逻辑结构。通过足够详细的标记,SGML 文档可以可靠地转换为其他标记系统,现在有一些书籍和期刊出版商就是这样做的:作者以多种格式提交材料,出版商将其转换为 SGML,然后使用 troff、TEX 或其他排版系统在后端生成可打印的页面。
然而,SGML 软件工具包仍然存在不足,且没有广泛标准化,因此为了实现最大的软件文档可移植性,最好的选择可能仍然是 troff 或 Texinfo 标记。对于手册页,如果要让 man 命令在所有地方都能正常工作,格式必须是 troff。最终,人们希望能够在任何一对标记系统之间进行可靠的自动转换。
以下是不同文档标记系统的特点对比表格:
| 标记系统 | 优点 | 缺点 | 适用场景 |
| ---- | ---- | ---- | ---- |
| nroff/troff | 传统的 Unix 手册页格式,可直接使用 man 等命令查看 | 标记不友好,学习曲线较陡 | 简短的编程文档,需要在 Unix 系统中广泛兼容 |
| Texinfo | 高级标记,支持超文本链接,可生成多种格式输出 | 对初学者来说可能较复杂 | 较长的软件文档,需要良好的在线导航功能 |
| SGML(及 HTML、XML) | 基于严格语法,可验证逻辑结构,可转换为其他格式 | 软件工具包不足,未广泛标准化 | 需要进行文档转换和多语言支持的场景 |
综上所述,在 Unix 系统中,无论是编写安全的 shell 脚本,还是为程序编写文档,都需要根据具体情况选择合适的方法和工具,以确保系统的安全和文档的质量。
Unix 系统中安全脚本编写与相关安全机制解析
9. 安全脚本编写技巧的操作流程
为了更清晰地展示编写安全 shell 脚本的技巧,下面给出具体的操作流程:
9.1 避免将当前目录(点)放入 PATH
-
操作步骤
:
-
打开用户的
.profile或.bashrc文件(具体取决于使用的 shell)。 -
检查
PATH变量的设置,确保不包含当前目录(.)。 - 如果包含,将其移除并保存文件。
-
重新加载配置文件,使更改生效。例如,对于
bash可以使用source ~/.bashrc命令。
-
打开用户的
9.2 保护 bin 目录
-
操作步骤
:
-
使用
ls -l命令查看$PATH中各个目录的权限。 -
对于可被其他用户写入的目录,使用
chmod命令更改权限。例如,将目录权限设置为755(所有者有读写执行权限,其他用户只有读和执行权限):
-
使用
chmod 755 /path/to/directory
3. 对于 bin 目录中的程序,同样检查并更改权限,确保只有所有者可以写入。
9.3 编码前进行设计
-
操作步骤
:
- 明确脚本的功能和目标,列出需要完成的任务。
- 设计脚本的结构,包括函数、变量和流程控制。
- 编写伪代码,描述脚本的执行逻辑。
- 根据伪代码编写实际的脚本代码,并添加错误处理机制。
9.4 检查所有输入参数的有效性
-
操作步骤
:
- 在脚本中使用条件语句检查输入参数的类型和范围。例如,检查输入是否为数字:
if! [[ "$input" =~ ^[0-9]+$ ]]; then
echo "输入必须是数字"
exit 1
fi
2. 对于其他类型的数据,使用 shell 的模式匹配功能进行验证。
9.5 检查所有可能返回错误的命令的错误代码
-
操作步骤
:
- 在调用可能返回错误的命令后,立即检查其返回值。例如:
command_output=$(command)
if [ $? -ne 0 ]; then
echo "命令执行失败"
exit 1
fi
2. 对于可能失败的命令,添加重试机制或其他错误处理逻辑。
9.6 不要信任传入的环境变量
-
操作步骤
:
- 在脚本中明确设置需要使用的环境变量。例如:
PATH=/usr/bin:/bin
IFS=' '
2. 避免直接使用传入的环境变量,除非经过验证和重置。
9.7 从已知位置开始
-
操作步骤
:
-
在脚本开头使用
cd命令切换到已知目录,并检查切换是否成功。例如:
-
在脚本开头使用
cd /path/to/known/directory || exit 1
9.8 使用命令的完整路径名
-
操作步骤
:
-
在脚本中使用命令的完整路径,而不是依赖
PATH查找。例如:
-
在脚本中使用命令的完整路径,而不是依赖
/usr/bin/grep "pattern" file.txt
10. 受限 shell 的使用场景和注意事项
受限 shell 主要用于来宾账户或需要限制用户操作的场景。以下是使用受限 shell 的一些注意事项:
-
设置受限 shell :
-
对于 ksh93,将
/etc/passwd中用户的登录 shell 改为rksh的完整路径,并确保 ksh93 可执行文件有一个名为rksh的链接。 -
对于 bash,将
/etc/passwd中用户的登录 shell 改为rbash的完整路径,并确保 bash 可执行文件有一个名为rbash的链接。
-
对于 ksh93,将
-
用户限制 :
- 受限用户无法更改工作目录、重定向输出、修改特定环境变量等。在设置受限 shell 时,需要考虑这些限制是否会影响用户的正常使用。
-
受限 shell 的环境应该在用户的
.profile文件中进行配置,确保在用户登录时生效。
-
安全性 :
- 受限 shell 虽然可以限制用户的操作,但并不能提供绝对的安全。仍然需要注意其他安全问题,如特洛伊木马和 setuid shell 脚本。
以下是受限 shell 使用的流程图:
graph TD;
A[用户登录] --> B{是否为受限用户};
B -- 是 --> C[执行 .profile 文件];
C --> D[启动受限 shell];
D --> E{用户操作};
E -- 更改目录 --> F[提示受限,操作失败];
E -- 重定向输出 --> G[提示受限,操作失败];
E -- 修改环境变量 --> H[提示受限,操作失败];
E -- 其他操作 --> I[检查是否受限];
I -- 是 --> J[提示受限,操作失败];
I -- 否 --> K[执行操作];
B -- 否 --> L[启动普通 shell];
11. 特洛伊木马的防范措施
为了防范特洛伊木马,除了前面提到的避免可写的 bin 目录和将点放入
PATH
外,还可以采取以下措施:
-
定期检查系统
:
-
使用
find命令查找系统中可写的 bin 目录和 shell 脚本。例如:
-
使用
find / -type d -perm -g+w -o -perm -o+w -print
find / -type f -name "*.sh" -perm -g+w -o -perm -o+w -print
- 定期检查系统日志,查看是否有异常的命令执行记录。
-
使用安全的
su命令 :-
养成使用
su - user命令切换用户的习惯,避免继承现有的PATH。
-
养成使用
-
加强用户教育 :
- 向用户普及特洛伊木马的危害和防范方法,提高用户的安全意识。
12. setuid shell 脚本的检查和处理
由于 setuid shell 脚本存在严重的安全风险,建议定期检查系统中是否存在此类文件,并采取相应的处理措施。
-
检查 setuid shell 脚本
:
-
使用
find命令查找系统中设置了 setuid 位的 shell 脚本。例如:
-
使用
find / -type f -perm -4000 -name "*.sh" -print
-
处理 setuid shell 脚本
:
- 如果发现系统中存在 setuid shell 脚本,建议将其删除或禁用。
- 如果确实需要使用具有特殊权限的脚本,可以考虑使用其他方式实现,如编写 C 程序并设置 setuid 位。
13. 文档编写的选择和建议
在为 Unix 程序编写文档时,根据不同的需求选择合适的标记系统是很重要的。以下是一些选择和建议:
-
对于简短的编程文档 :
-
如果需要在 Unix 系统中广泛兼容,建议使用 nroff/troff 标记编写手册页。可以使用
man命令直接查看,方便用户获取信息。 - 编写手册页时,遵循标准的格式和规范,确保文档的一致性和可读性。
-
如果需要在 Unix 系统中广泛兼容,建议使用 nroff/troff 标记编写手册页。可以使用
-
对于较长的软件文档 :
-
如果需要良好的在线导航功能和多种格式输出,建议使用 Texinfo 标记。可以使用
makeinfo程序生成 ASCII、HTML、XML 等格式的文档。 - 在编写 Texinfo 文档时,合理使用章节、段落、列表等标记,提高文档的结构清晰度。
-
如果需要良好的在线导航功能和多种格式输出,建议使用 Texinfo 标记。可以使用
-
对于需要进行文档转换和多语言支持的场景 :
- 可以考虑使用 SGML(及 HTML、XML)标记。虽然 SGML 软件工具包存在不足,但基于严格的语法可以进行逻辑结构验证和文档转换。
- 在使用 SGML 标记时,确保使用合适的工具和模板,提高文档的编写效率。
14. 总结与展望
在 Unix 系统中,安全脚本编写和文档编写都是至关重要的工作。编写安全的 shell 脚本可以有效避免系统受到攻击,而高质量的文档可以帮助用户更好地使用程序。
通过遵循编写安全 shell 脚本的技巧,使用受限 shell 限制用户操作,防范特洛伊木马,避免使用 setuid shell 脚本,以及选择合适的文档标记系统,可以提高 Unix 系统的安全性和易用性。
未来,随着技术的不断发展,Unix 系统的安全机制和文档编写工具可能会不断改进和完善。我们需要持续关注这些变化,不断学习和掌握新的知识和技能,以适应不断变化的安全需求和文档编写要求。同时,加强用户的安全意识和文档编写能力的培训,也是保障系统安全和提高软件质量的重要环节。
总之,在 Unix 系统的使用和管理中,安全和文档是两个不可忽视的方面,只有做好这两方面的工作,才能充分发挥 Unix 系统的优势。
超级会员免费看
1982

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



