[ 上一章 ] [ 目录 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ A ] [ B ] [ C ] [ D ] [ E ] [ F ] [ G ] [ H ] [ 下一章 ]
本章介绍了一些针对编写 Debian 软件包的开发者的最佳安全编码实践。如果您对安全编码非常感兴趣,我建议您阅读 David Wheeler 的 Linux 和 Unix 安全编程 HOWTO 以及 Mark G. Graff 和 Kenneth R. van Wyk 编写的 安全编码:原则与实践 (O'Reilly, 2003)。
正在打包软件的开发者应尽最大努力确保软件的安装或使用不会给安装它的系统或其用户带来安全风险。
为了做到这一点,他们应尽最大努力审查软件包的源代码,并在发布软件或分发新版本之前检测可能引入安全漏洞的任何缺陷。人们普遍认为,在软件开发的不同阶段,修复漏洞的成本会不断增加,因此在设计阶段修复漏洞比在软件已部署并处于维护模式时修复漏洞更容易(且成本更低)(一些研究表明,在后一阶段,成本要高出 六十 倍)。尽管有一些工具试图自动检测这些缺陷,但开发者应努力学习不同类型的安全缺陷,以便理解它们,并能够在他们(或其他人)编写的代码中发现它们。
导致安全漏洞的编程错误通常包括:缓冲区溢出、格式字符串溢出、堆溢出和整数溢出(在 C/C++ 程序中)、临时 符号链接竞争条件(在脚本中)、目录遍历 和命令注入(在服务器中)以及 跨站脚本 和 SQL 注入漏洞(在面向 Web 的应用程序的情况下)。有关安全漏洞的更完整信息,请查看 Fortify 的 软件安全错误分类。
除非您是软件使用的编程语言方面的专家,否则其中一些问题可能不容易发现,但有些安全问题很容易检测和修复。例如,只需运行 grep -r "/tmp/" . 即可轻松找到由于滥用临时目录而导致的临时竞争条件。可以审查这些调用,并将使用临时目录的硬编码文件名替换为在 shell 脚本中调用 mktemp 或 tempfile,在 Perl 脚本中调用 File::Temp(3perl),或在 C/C++ 中调用 tmpfile(3)。
有一系列工具可用于协助安全代码审查阶段。这些工具包括 rats、flawfinder 和 pscan。有关更多信息,请阅读 Debian 安全审计团队使用的工具列表。
在打包软件时,开发者必须确保他们遵循通用的安全原则,包括
软件以其所需的最低权限运行
软件包不安装 setuid 或 setgid 的二进制文件。Lintian 会警告 setuid、setgid 和 setuid 和 setgid 二进制文件。
软件包提供的守护进程以低权限用户身份运行(参见 第 9.2 节,为软件守护进程创建用户和组)
系统中运行的已编程(即 cron)任务不应以 root 身份运行,或者,如果以 root 身份运行,则不应执行复杂的任务。
如果您必须执行上述任何操作,请确保可能以更高权限运行的程序已进行安全漏洞审核。如果您不确定或需要帮助,请联系 Debian 安全审计团队。对于 setuid/setgid 二进制文件,请遵循 Debian 策略中关于 权限和所有者 的部分
有关更多信息,特别是关于安全编程的信息,请确保您阅读(或引导您的上游阅读) Linux 和 Unix 安全编程 HOWTO 和 Build Security In 门户网站。
如果您的软件运行的守护进程不需要 root 权限,则需要为其创建一个用户。Debian 软件包可以使用两种类型的用户:静态 uid(由 base-passwd 分配,有关 Debian 中静态用户的列表,请参见 第 12.1.12 节,操作系统用户和组)和系统用户范围内分配的动态 uid。
在第一种情况下,您需要向 base-passwd 请求用户或组 id。一旦该用户可用,软件包需要发布,并在依赖关系中包含对 base-passwd 软件包的正确版本依赖。
在第二种情况下,您需要在 preinst 或 postinst 中创建系统用户,并使软件包依赖于 adduser (>= 3.11)。
以下示例代码在软件包安装或升级时创建守护进程将以其身份运行的用户和组
[...]
case "$1" in
install|upgrade)
# If the package has default file it could be sourced, so that
# the local admin can overwrite the defaults
[ -f "/etc/default/packagename" ] && . /etc/default/packagename
# Sane defaults:
[ -z "$SERVER_HOME" ] && SERVER_HOME=server_dir
[ -z "$SERVER_USER" ] && SERVER_USER=server_user
[ -z "$SERVER_NAME" ] && SERVER_NAME="Server description"
[ -z "$SERVER_GROUP" ] && SERVER_GROUP=server_group
# Groups that the user will be added to, if undefined, then none.
ADDGROUP=""
# create user to avoid running server as root
# 1. create group if not existing
if ! getent group | grep -q "^$SERVER_GROUP:" ; then
echo -n "Adding group $SERVER_GROUP.."
addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true
echo "..done"
fi
# 2. create homedir if not existing
test -d $SERVER_HOME || mkdir $SERVER_HOME
# 3. create user if not existing
if ! getent passwd | grep -q "^$SERVER_USER:"; then
echo -n "Adding system user $SERVER_USER.."
adduser --quiet \
--system \
--ingroup $SERVER_GROUP \
--no-create-home \
--disabled-password \
$SERVER_USER 2>/dev/null || true
echo "..done"
fi
# 4. adjust passwd entry
usermod -c "$SERVER_NAME" \
-d $SERVER_HOME \
-g $SERVER_GROUP \
$SERVER_USER
# 5. adjust file and directory permissions
if ! dpkg-statoverride --list $SERVER_HOME >/dev/null
then
chown -R $SERVER_USER:adm $SERVER_HOME
chmod u=rwx,g=rxs,o= $SERVER_HOME
fi
# 6. Add the user to the ADDGROUP group
if test -n $ADDGROUP
then
if ! groups $SERVER_USER | cut -d: -f2 | \
grep -qw $ADDGROUP; then
adduser $SERVER_USER $ADDGROUP
fi
fi
;;
configure)
[...]
您必须确保 init.d 脚本文件
启动守护进程时降低权限:如果软件本身不执行 setuid(2) 或 seteuid(2) 调用,则可以使用 start-stop-daemon 的 --chuid 调用。
仅当用户 id 匹配时才停止守护进程,您可以为此使用 start-stop-daemon 的 --user 选项。
如果用户或组不存在,则不运行
if ! getent passwd | grep -q "^server_user:"; then
echo "Server user does not exist. Aborting" >&2
exit 1
fi
if ! getent group | grep -q "^server_group:" ; then
echo "Server group does not exist. Aborting" >&2
exit 1
fi
如果软件包创建了系统用户,则可以在 postrm 中清除软件包时将其删除。但这也有一些缺点。例如,它创建的文件将成为孤立文件,并且将来可能会被分配相同 uid 的新系统用户接管[63]。因此,在清除时删除系统用户还不是强制性的,并且取决于软件包的需求。如果不确定,可以在安装软件包时通过询问管理员首选操作来处理此操作(即通过 debconf)。
以下示例代码[64] 仅在 uid 在动态分配的系统 uid 范围内且 gid 属于系统组时,才删除之前创建的用户和组
case "$1" in
purge)
[...]
# find first and last SYSTEM_UID numbers
for LINE in `grep SYSTEM_UID /etc/adduser.conf | grep -v "^#"`; do
case $LINE in
FIRST_SYSTEM_UID*)
FIST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
;;
LAST_SYSTEM_UID*)
LAST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
;;
*)
;;
esac
done
# Remove system account if necessary
CREATEDUSER="server_user"
if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then
if USERID=`getent passwd $CREATEDUSER | cut -f 3 -d ':'`; then
if [ -n "$USERID" ]; then
if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \
[ "$USERID" -le "$LAST_SYSTEM_UID" ]; then
echo -n "Removing $CREATEDUSER system user.."
deluser --quiet $CREATEDUSER || true
echo "..done"
fi
fi
fi
fi
# Remove system group if necessary
CREATEDGROUP=server_group
FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='`
if [ -n "$FIST_USER_GID" ] then
if GROUPGID=`getent group $CREATEDGROUP | cut -f 3 -d ':'`; then
if [ -n "$GROUPGID" ]; then
if [ "$FIST_USER_GID" -gt "$GROUPGID" ]; then
echo -n "Removing $CREATEDGROUP group.."
delgroup --only-if-empty $CREATEDGROUP || true
echo "..done"
fi
fi
fi
fi
[...]
使用权限受限的用户运行程序可确保任何安全问题都不会损坏整个系统。这也遵循最小权限原则。另请考虑,除了以非 root 身份运行外,您还可以通过其他机制限制程序中的权限[65]。有关更多信息,请阅读 Linux 和 Unix 安全编程 HOWTO 书籍的 最小化权限 章节。
[ 上一章 ] [ 目录 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ A ] [ B ] [ C ] [ D ] [ E ] [ F ] [ G ] [ H ] [ 下一章 ]
Debian 安全手册
版本:3.13,Sun, 08 Apr 2012 02:48:09 +0000jfs@debian.org