Bash 安全地从文件中获取变量

Posted

技术标签:

【中文标题】Bash 安全地从文件中获取变量【英文标题】:Bash Getting variables from a file safely 【发布时间】:2016-08-17 03:08:53 【问题描述】:

我有一个将变量存储在 .txt 文件中以供以后使用的脚本。我想安全地从文件中检索这些变量。

我现在是如何设置的:

设置.txt

var1=value1
var2=value2
...

脚本

for i in $(cat Settings.txt); do $i done
# So now "var1" has the value "value1", and so on

这可行,但很危险,因为有人可能会通过该 txt 文件注入代码。

我知道source 命令,但这也有同样的问题。那么,如何安全地实现相同的功能呢?

【问题讨论】:

safely 是什么意思?停止执行任何代码? @sobolevn 是的,我不希望任何代码会意外地从 txt 文件中执行。如果文件有rm -fr / 行,它会造成一些不必要的损害:/ @BonBon : 变量总是以var 开头吗? @sjsam 不,可以是任何带有大小写字符和“_”的变量名。该值可以是 1、0 或一些没有特殊字符的短字符串。 【参考方案1】:

如果您不希望单独的验证和变量创建步骤:

更新,基于declare:更简单但仍然安全的方法是使用declare 内置函数来定义变量:

#!/usr/bin/env bash

# Read the file line by line.
while read -r line; do
  declare -- "$line"
done <<'EOF'
  var1=value1
  var2=value2
EOF

declare 命令失败,输入行不是有效的 shell 变量赋值,但它失败了安全,因为该行永远不会被评估为 命令 em>.

请注意,这些值被读取为 文字,与文件中的定义完全相同(除了删除尾随空格)。

如果您还想支持单引号或双引号值,请改用以下declare 命令:declare -- "$(xargs -I printf %s &lt;&lt;&lt;"$line")" 但请注意,不支持在值中使用相同类型的嵌入、转义引号(这是xargs的限制)。


原始答案,基于printf -v

#!/usr/bin/env bash

# Read the file line by line.
while read -r line; do
  # Split the line into name and value using regex matching.
  if [[ $line =~ ^([^=]+)=(.*)$ ]]; then
    # $BASH_REMATCH[1] now contains the variable name, 
    # $BASH_REMATCH[2] the value.
    # Use printf -v to store the value in a variable.
    printf -v "$BASH_REMATCH[1]" %s "$BASH_REMATCH[2]"
  fi
done <<'EOF'
  var1=value1
  var2=value2
EOF

# Print the variables that were created (based on name prefix `var`).
for name in $!var*; do
  printf '%s=%s\n' "$name" "$!name"
done

请注意,这些值被读取为文字,与文件中的定义完全相同(除了删除尾随空格)。

如果存在单引号或双引号的值并且您想删除引号,请改用以下printf -v 命令:printf -v "$BASH_REMATCH[1]" %s "$(xargs -I printf %s &lt;&lt;&lt;"$BASH_REMATCH[2]")" 但请注意,支持具有相同类型的嵌入、转义引号的带引号的字符串。

应该可以安全使用,因为 printf -v 用于创建变量 - shell 不会直接获取赋值语句,而这正是可能发生注入的地方。

简单地跳过未被识别为变量赋值的行。

Regex ^([^=]+)=(.*)$ 匹配任何以 (^) 开头且至少有 1 个 (+) 字符而不是 = ([^=]) 的行,紧随其后的是 =,然后是任何剩余的序列字符 (.*) 到行尾 ($)。 ([^=]+)(.*) 周围的括号确保捕获的子字符串保存在特殊的 Bash 数组变量 $BASH_REMATCH[@] 中,从索引 1 开始。

为简单起见,没有尝试预先验证变量名,这意味着printf -v 命令稍后可能会失败。

【讨论】:

@BonBon:很高兴听到这个消息;我突然想到使用declare 而不是printf -v 可以实现更简单的解决方案;请看我的更新。【参考方案2】:

您可以在采购之前检查变量分配格式:

#!/bin/bash

file=Settings.txt
regex_varname='^[a-zA-Z0-9_]\+\'
regex_varvalue='[a-zA-Z0-9]\+$'

is_safe_var() 
  while read var; do
    grep -q $regex_varname=$regex_varvalue <<< "$var" || return 1
  done < "$file"


is_safe_var && source "$file" || echo "Break"

变量$regex_varname$regex_varvalue 是为变量授权名称和值分配模式:

^[a-zA-Z0-9_] : 以一个或多个字母数字字符开头的变量名或_ [a-zA-Z0-9]\+$ : 变量值以一个或多个字母数字字符结尾

函数is_safe_var 中的循环检查Settings.txt 的每一行是否与模式$regex_varname=$regex_varvalue 匹配。

如果有一行测试失败,它从函数返回,错误代码和echo "Break",否则Settings.txt是source。

注意:您可以在变量名称和值中使用授权字符完成字符范围[a-zA-Z0-9_][a-zA-Z0-9]

【讨论】:

感谢您的回答!您能否添加一些解释您的正则表达式的作用,因为它有点难以理解。

以上是关于Bash 安全地从文件中获取变量的主要内容,如果未能解决你的问题,请参考以下文章

即使文档中缺少请求的字段,如何安全地从 cloud-firestore 获取数据?

如何安全地提前退出 bash 脚本?

markdown 安全地从密码存储区获取SSH密钥的密码

如何从上一个 bash 命令中获取倒数第二个参数? (在交互式 bash shell 中)

有效地从 ArrayList 中获取子列表

用于获取多个存在的变量的 Bash 快捷方式