我强烈建议不要使用标准命令 shell 脚本语言(例如 csh、sh 和 bash)来编写 setuid/setgid 安全代码。 某些系统(例如 Linux)完全禁用 setuid/setgid shell 脚本,因此创建 setuid/setgid shell 脚本会产生不必要的移植性问题。 在一些旧系统中,由于竞争条件(如 第 3.1.3 节 中讨论的),它们在本质上是不安全的。 即使对于其他系统,这也不是一个好主意。
实际上,在很多情况下,根本不应该将 shell 脚本语言用于安全程序。 标准命令 shell 以容易受到不明显的输入影响而闻名——这通常是因为命令 shell 的设计目的是为了尝试为交互式用户“自动”执行操作,而不是为了防御坚定的攻击者。 Shell 程序适用于不需要安全的程序(例如,它们以与非特权用户相同的权限运行,并且不接受“不受信任”的数据)。 当它们以特权运行时,它们也可能很有用,只要所有输入(例如,文件、目录、命令行、环境等)都来自受信任的用户——这就是为什么它们经常在启动/关闭脚本中非常成功地使用。
在存在恶意输入的情况下编写安全的 shell 程序比在许多其他语言中更难,因为 shell 会受到很多因素的影响。 例如,“隐藏”的环境变量(例如,ENV、BASH_ENV 和 IFS 值)会影响它们的运行方式,甚至在脚本可以执行之前就执行任意用户定义的代码。 即使是可执行文件的文件名或目录内容等事物也可能影响执行。 如果攻击者可以创建包含某些控制字符(例如,换行符)、空格、shell 元字符或以破折号(选项标志语法)开头的文件名,通常有很多方法可以利用它们。 例如,在许多 Bourne shell 实现中,执行以下操作将授予 root 访问权限(感谢 NCSA 描述了这个漏洞)
% ln -s /usr/bin/setuid-shell /tmp/-x % cd /tmp % -x |
如果您仍然坚持使用 shell 脚本语言,至少将脚本放在一个无法移动或更改的目录中。 在脚本的早期将 PATH 和 IFS 设置为已知值; 实际上,应该在调用脚本之前清理环境。 此外,在非常早期的时候,“cd”到一个安全的目录。 仅使用受信任用户控制的目录中的数据,例如 /etc,这样攻击者就无法将恶意命名的文件插入到这些目录中。 务必引用在命令行上传递的每个文件名,例如,使用“$1”而不是 $1,因为带有空格的文件名将被拆分。 在可以的情况下,使用“--”调用命令以禁用其他选项,因为攻击者可能会创建或传递以破折号开头的文件名,希望欺骗程序将其作为选项处理。 要特别注意嵌入其他字符(例如,换行符和其他控制字符)的文件名。 尤其要仔细检查输入文件名,并严格限制允许的文件名。
如果您不介意将您的程序限制为仅使用 GNU 工具(或者如果您检测到 GNU 工具可用并选择使用它们),您可能需要使用 NIL 字符作为文件名终止符,而不是换行符。 通过使用 NIL 字符而不是空格或换行符,处理有问题的文件名(例如,那些嵌入换行符的文件名)要简单得多。 一些输出或输入文件名的 GNU 工具可以使用这种格式,而不是更常见的“每行一个文件名”格式。 不幸的是,此选项的名称在工具之间不一致; 对于许多工具,此选项的名称是“--null”或“-0”。 GNU 程序 xargs 和 cpio 允许使用 --null 或 -0,tar 使用 --null,find 使用 -print0,grep 使用 --null 或 -Z,而 sort 使用 -z 或 --zero-terminated。 那些发现这种不一致性特别令人不安的人被邀请向 GNU 作者提供补丁; 我建议确保每个程序都支持“--null”,因为这似乎是最常见的选项名称。 例如,这是一种将文件移动到目标目录的方法,即使可能有很多文件,并且有些文件可能具有带有嵌入换行符的奇怪名称(感谢 Jim Dennis 提醒我这一点)
find . -print0 | xargs --null mv --target-dir=$TARG |
类似地,我建议不要信任“受限 shell”来实施安全策略。 受限 shell 是有意阻止用户执行大量活动的 shell —— 它们的目标是强制用户仅运行少量程序。 受限 shell 可以作为深度防御措施很有用,但众所周知,受限 shell 很难正确配置,并且配置后的 shell 经常是可破坏的。 例如,一些受限 shell 将首先在非受限模式下运行某些文件(例如,“.profile”)——如果用户可以更改此文件,他们可以强制执行该代码。 应该将受限 shell 设置为仅运行少量程序,但是如果这些程序中的任何一个具有“shell 转义”以允许用户运行更多程序,则攻击者可以使用这些 shell 转义来逃脱受限 shell。 即使程序没有 shell 转义,也很可能可以将各种程序组合在一起(以及 shell 的功能)以逃避限制。 当然,如果您不设置受限 shell 的 PATH(并允许任何程序运行),那么攻击者可以使用许多程序的 shell 转义(包括文本编辑器、邮件程序等)。 问题在于 shell 的目的是运行其他程序,但是这些其他程序可能允许意外的操作——并且 shell 不会介入来阻止这些操作。