5.7.3 依赖命令与路径安全
依赖命令与路径安全的核心是可控、可预期与最小化信任:脚本必须显式声明依赖、验证命令来源,避免被环境变量或同名恶意程序劫持。
关键原则与命令解释#
- 固定 PATH:只保留可信目录,避免包含当前目录
. export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"- 校验命令存在:
command -v rsync:返回命令路径,非零表示缺失type -a rsync:列出所有匹配路径- 校验真实路径与权限:
readlink -f /usr/bin/rsync:解析真实路径stat -c '%U %A %n' /usr/bin/rsync:查看属主与权限- 清理环境变量:
env -i PATH=... /bin/bash script.sh:空环境运行- 重点变量:
LD_PRELOAD、IFS、BASH_ENV - 命令缓存:
hash -r:刷新 Shell 命令路径缓存
可执行示例:依赖检查与路径锁定#
文件路径:/opt/scripts/backup.sh
#!/usr/bin/env bash
set -euo pipefail
# 1) 固定 PATH,避免从不可信目录加载
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# 2) 清理危险环境变量
unset LD_PRELOAD IFS BASH_ENV
# 3) 依赖清单
DEPS=(/usr/bin/rsync /usr/bin/ssh /bin/date)
# 4) 检查命令是否存在与权限
for cmd in "${DEPS[@]}"; do
if [[ ! -x "$cmd" ]]; then
echo "ERROR: missing or not executable: $cmd" >&2
exit 1
fi
echo "OK: $(stat -c '%U %A %n' "$cmd")"
done
# 5) 记录版本,确保一致性
/usr/bin/rsync --version | head -n1
/usr/bin/ssh -V 2>&1 | head -n1
# 6) 业务逻辑(示例:备份)
SRC="/data/"
DST="backup@10.0.0.10:/data/backup/"
/usr/bin/rsync -avz --delete "$SRC" "$DST"
echo "Backup finished at $(/bin/date '+%F %T')"
预期效果:
- 缺少依赖直接报错退出
- 命令来源与权限输出清晰
- 备份命令使用绝对路径,避免路径劫持
安装与依赖来源(示例)#
使用系统包管理器安装并校验来源:
# Debian/Ubuntu
sudo apt update
sudo apt install -y rsync openssh-client
apt-cache policy rsync | head -n3
# RHEL/CentOS/Rocky
sudo yum install -y rsync openssh-clients
rpm -qi rsync | head -n5
验证签名与来源(示例):
rpm -q --qf '%{SIGPGP:pgpsig}\n' rsync
排错指南(常见问题与定位命令)#
-
命令被同名程序劫持
- 现象:脚本执行异常、输出不同
- 排查:
bash type -a rsync command -v rsync readlink -f "$(command -v rsync)"
- 解决:固定 PATH、使用绝对路径、删除可疑文件 -
更新命令后仍执行旧路径
- 排查:
bash hash
- 解决:
bash hash -r -
环境变量被污染导致行为异常
- 排查:
bash env | egrep 'LD_PRELOAD|IFS|BASH_ENV'
- 解决:env -i启动或unset
练习#
- 写一个脚本
check_deps.sh,检查curl、tar、gzip是否存在,并输出路径与权限。 - 在
~/bin放置一个假rsync,观察type -a rsync的输出变化,然后通过固定 PATH 解决。 - 使用
env -i运行脚本,验证LD_PRELOAD对脚本行为的影响是否被消除。