【问题标题】:are there security issues with using eval on an environment variable in a bash script?在 bash 脚本中的环境变量上使用 eval 是否存在安全问题?
【发布时间】:2020-06-27 05:11:13
【问题描述】:

我有一个 Bash 脚本,我在其中调用 rsync 以执行到远程服务器的备份。要指定备份我的下载文件夹,我将"'${HOME}/Downloads'" 作为参数传递给rsync,它会产生输出:

rsync -avu '/Volumes/Norman Data/Downloads' me@example.com:backup/

使用上面扩展的变量(通过终端或脚本)运行命令可以正常工作,但是由于扩展变量中的空间以及引号(单个刻度)被包含在变量中时被忽略的事实作为参数的一部分传递(请参阅here),我能让它不阻塞空间的唯一方法是:

stmt="rsync -avu '${HOME}/Downloads' me@examle.com:backup/"
eval ${stmt}

在任何不是该脚本 100% 私有的东西上运行 eval 似乎会出现一些漏洞。我认为我应该以不同的方式做这件事是否正确?如果是这样,对于 bash-script-beginner 的任何提示将不胜感激。


** EDIT ** - 实际上我有更多的用例。上面的例子。对于传递的路径,我有一个数组,每个都包含空格,然后我将它们组合成 1 个类似的字符串

include_paths=(
  "'${HOME}/dir_a'"
  "'${HOME}/dir_b' --exclude=video"
)

for item in "${include_paths[@]}"
  do
    inc_args="${inc_args}" ${item}
  done

inc_args 计算结果为 '/Volumes/Norman Data/me/dir_a' '/Volumes/Norman Data/me/dir_b' --exclude=video

然后我尝试将其作为参数传递给rsync,但单个刻度被读取为文字,并且由于空格而在第一个/Volumes/Norman 之后中断。

rsync -avu "${inc_args}" me@example.com:backup/

使用 eval 似乎将单个刻度读取为引号并执行:

rsync -avu '/Volumes/Norman Data/me/dir_a' '/Volumes/Norman Data/me/dir_b' --exclude=video me@example.com:backup/

就像我需要的那样。我似乎找不到其他工作方式。


** 编辑 2 - 解决方案 **
所以我需要做的第一件事就是将include_paths 数组修改为:

  • 从双引号项中删除单个刻度
  • 将任何特定于路径的标志(例如--exclude)直接移动到它们应该应用到的路径之后的项目中

然后我构建了一个包含rsync 命令及其选项的数组,添加了扩展的include_paths 和exclude_paths 数组以及到远程主机的连接字符串。

最后扩展了该数组,它运行了我的整个正确引用的rsync 命令。最后修改后的数组include_paths是:

include_paths=(
  "${HOME}/dir_a"
  "${HOME}/dir_b"
  "--exclude=video"
  "${HOME}/dir_c"
)

我把所有东西放在一起:

cmd=(rsync -auvzP)
for item in "${exclude_paths[@]}"
  do  
    cmd+=("--exclude=${item}")
  done

for item in "${include_paths[@]}"
  do
   cmd+=("${item}")
  done

cmd+=("me@example.com:backup/")

set -x
"${cmd[@]}"

【问题讨论】:

  • 你可以像$("$stmt")一样执行
  • @DigvijayS 这根本行不通。双引号告诉外壳程序将整个字符串——参数和所有——作为命令名,而不是作为命令名后跟参数。然后$( ) 使shell 尝试执行任何能够作为第二个命令打印的内容。
  • 是否有理由将命令放在变量中,而不是直接执行?如果是这样,取决于原因是什么,函数或数组将是一个更好的主意。见BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!
  • @GordonDavisson 有正确的想法。 eval 仅在非常特殊的情况下才真正需要,例如 eval "$(ssh-agent)"。
  • @Daveh0 将它们保存为数组形式,而不是转换为单个字符串。不要不要在数组元素中嵌入引号(引号围绕数据,而不是在数据中),并且不要将多个项目放在一个数组项(就像您对“dir_b”和“video”所做的那样)。您可以使用 "${array[@]/#/prefix}" 成语为每个数组元素添加前缀。请参阅here 以获取非常相似情况的示例。

标签: bash security environment-variables eval


【解决方案1】:

对命令/选项使用数组而不是普通变量。

stmt=(rsync -avu "${HOME}/Dowloads" me@example.com:backup/)

使用内置的command执行它

command "${stmt[@]}"

...或者我个人只是将选项/参数放在一个数组中。

options=(-avu "${HOME}/Download" me@example.com:backup/)

使用rsync执行它

rsync "${options[@]}"

如果您有更新版本的 bash,它支持额外的 P.E.参数扩展,那么你可能会引用数组。

options=(-avu "${HOME}/Download" me@example.com:backup/)

通过应用 P.E. 检查输出

echo "${options[@]@Q}"

应该打印

'-avu' '/Volumes/Norman Data/Downloads' 'me@examle.com:backup/'

那么你就可以了

rsync "${options[@]@Q}"

【讨论】:

  • 查看我的编辑 - 我有多个文件/路径要传递给 rsync - 这似乎会导致问题,因为每个路径都需要被引用。但我什至无法让opts=(-auv "'${HOME}/Desktop'" me@example.com:backup/) rsync "${opts[@]}" 工作。给出错误:rsync: link_stat "/Volumes/Norman Data/me/Scripts/backup/'/Volumes/Norman Data/me/Desktop'" failed: No such file or directory (2)。有什么想法吗?
  • quoute ${opts[@]} 也 "'${HOME}/dir_b' --exclude=video" 是一个参数而不是两个。
  • echo "${opts[@]}" 确实按预期打印-auv '/Volumes/Norman Data/me/Desktop' me@example.com:backup/。难住了。
  • 我做了 - 在评论中忘记添加 - 已修复
  • 未检测到问题
猜你喜欢
  • 1970-01-01
  • 2017-10-26
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
  • 2017-12-13
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多