【问题标题】:Bash Getting variables from a file safelyBash 安全地从文件中获取变量
【发布时间】: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 或一些没有特殊字符的短字符串。

标签: bash parsing variable-assignment


【解决方案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]

【讨论】:

  • 感谢您的回答!您能否添加一些解释您的正则表达式的作用,因为它有点难以理解。
猜你喜欢
  • 1970-01-01
  • 2012-01-30
  • 2020-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2011-06-14
  • 1970-01-01
相关资源
最近更新 更多