【问题标题】:exporting environment variables with spaces using jq使用 jq 导出带空格的环境变量
【发布时间】:2018-07-23 15:10:32
【问题描述】:

所以,我正在尝试导出一个来自返回 json 值的 api 的环境变量。想使用 jq 只做一个衬里,但如果值有空格我无法让它工作

尝试不将值括在引号中

/app/src $ $(echo '{"params":[{ "Name":"KEY","Value":"value with space"}]}' | jq
 -r '.params[] | "export " + .Name + "=" + .Value')
/app/src $ printenv KEY
value
/app/src $ 

接下来,我尝试将值用引号括起来

/app/src $ $(echo '{"params":[{ "Name":"KEY","Value":"value with space"}]}' | jq
 -r '.params[] | "export " + .Name + "=\"" + .Value + "\""')
sh: export: space": bad variable name
/app/src $ 

【问题讨论】:

  • 颤抖。这里有龙,我所说的“龙”是指“外壳注入漏洞”。请参阅BashFAQ #50,了解为什么不带引号的扩展无法生成带空格的 shell 命令。
  • @CharlesDuffy 是的,我明白,我只是希望有一个解决方法。我永远不会使用一些公共 api 或未知 json 来做这件事,但这一切都来自我们的内部网络私有凭证存储。
  • 上下文是什么?通常,更好的做法是将 JSON 键/值对加载到 bash 中的关联数组中——这样你就不必担心覆盖对 shell 本身有意义的名称(并且全大写键使其成为特定的危险)。
  • 顺便说一句,更好的形式是foo=bar; export foo 而不是export foo=bar,这样我们只是使用简单的赋值而不是调用单独的命令(是的,它是内置的,但在解析器级别,它被视为命令)。不幸的是,我们在这里需要后一种形式,因为jq 转义名称的方式会阻止它们被视为赋值(它的输出看起来像'foo'='bar',并且引用名称的任何部分都会阻止shell从看到一个作业)。

标签: bash environment-variables jq


【解决方案1】:

对于以下所有内容,我假设:

json='{"params":[{ "Name":"KEY","Value":"value with space"}]}'

可以做到,但只有在您相信自己的输入的情况下

使用eval 的解决方案可能如下所示:

eval "$(jq -r '.params[] | "export \(.Name | @sh)=\(.Value | @sh)"' <<<"$json")"

jq 中的 jq 内置函数将内容转义为 bash 中的 eval-safe,然后 eval 调用确保内容经过所有解析阶段(因此 @ 发出的数据中的文字引号987654328@变成语法)。

但是,所有允许分配任意 shell 变量的解决方案都存在固有的安全问题,因为可以利用设置变量(如 PATHLD_LIBRARY_PATHLD_PRELOAD 等)的能力来执行任意代码。


更好的形式是生成一个 NUL 分隔的键/值列表...

build_kv_nsv() {
  jq -j '.params[] |
    ((.Name | gsub("\u0000"; "")),
     "\u0000",
     (.Value | gsub("\u0000"; "")),
     "\u0000")'
}

...或者填充关联数组...

declare -A content_received=( )
while IFS= read -r -d '' name && IFS= read -r -d '' value; do
  content_received[$name]=$value
done < <(build_kv_nsv <<<"$json")

# print the value of the populated associative array
declare -p content_received

...或者使用带有前缀的命名空间来保证安全。

while IFS= read -r -d '' name && IFS= read -r -d '' value; do
  printf -v "received_$name" %s "$value" && export "received_$name"
done < <(build_kv_nsv <<<"$json")

# print names and values of our variables that start with received_
declare -p "${!received_@}" >&2

【讨论】:

  • 谢谢!我了解这里的可利用问题,并且不会将其用于某些未知的 api 或 json。场景是在一个私有网络中,我们正在从我们的凭证参数存储中检索加密的凭证
  • 信任来自内部系统的内容是一种允许从一个受感染的系统横向移动到其他尚未受感染的主机的好方法......虽然被授予,但如果您的凭据存储被弹出,您可能无论如何,在糟糕的情况下。
  • 但请记住,可以设置任意环境变量的攻击者可以创建导出函数,设置 LD_LIBRARY_PATH 以覆盖随机 C 调用,如果他们可以将他们选择的文件放到您的主机上(即使在像/tmp这样的其他无害的地方),否则会造成很多恶作剧。
  • 感谢所有更新。凭证存储和导出强制在所有键上加上前缀,所以我不必担心与系统环境的冲突。还将添加其他安全预防措施。如果必须的话,我会把它构建成一个脚本,只是试图制作一个简单的衬里。
  • @ikegami,received_$name 方法创建received_LD_LIBRARY_PATH,这是安全的。 content_received[LD_LIBRARY_PATH] 方法不会改变LD_LIBRARY_PATH
【解决方案2】:

如果已知这些值不包含(原始)换行符,并且您可以访问mapfile,则可能值得考虑使用它,例如

$ json='{"params":[{ "Name":"KEY","Value":"value with space"}]}'
$ mapfile -t KEY < <( jq -r '.params[] | .Value' <<< "$json" )
$ echo N=${#KEY[@]}
N=1

如果这些值可能包含(原始)换行符,那么您需要一个带有 -d 选项的 mapfile 版本,如下所示:

$ json='{"params":[{ "Name":"KEY1","Value":"value with space"}, { "Name":"KEY2","Value":"value with \n newline"}]}'
$ mapfile -d $'\0' KEY < <( jq -r -j '.params[] | .Value + "\u0000"' <<< "$json" )
$ echo N=${#KEY[@]}
N=2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-03
    • 2019-09-27
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    相关资源
    最近更新 更多