Bash 安全地从文件中获取变量
Bash Getting variables from a file safely
我有一个脚本可以将变量存储在 .txt 文件中供以后使用。我想从文件中安全地检索这些变量。
我现在是如何设置的:
Settings.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
命令,但它也有同样的问题。那么,如何安全的实现同样的功能呢?
您可以在采购前检查变量赋值格式:
#!/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
.
如果一行未通过测试,它 returns 来自带有错误代码和 echo "Break"
的函数,否则 Settings.txt
是来源。
注意:您可以在变量名称和值中使用授权字符来完成字符范围 [a-zA-Z0-9_]
和 [a-zA-Z0-9]
。
如果您不想单独执行验证和变量创建步骤:
更新,基于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 变量赋值而失败,但安全地 失败该行永远不会被评估为 command.
请注意,这些值被读取为 文字 ,与文件中定义的完全相同(删除尾随空格除外)。
如果您还想支持单引号或双引号值,请改用以下 declare
命令:
declare -- "$(xargs -I {} printf %s {} <<<"$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 {} <<<"${BASH_REMATCH[2]}")"
但请注意,嵌入、转义引号的引用字符串不受支持。
应该可以安全使用,因为 printf -v
用于创建变量 - shell 不直接作为赋值语句的来源,这是可能发生注入的地方.
简单地跳过未识别为变量赋值的行。
正则表达式 ^([^=]+)=(.*)$
匹配任何以 (^
) 开头的至少 1 (+
) 个字符而不是 =
([^=]
),紧接着是 =
,接着是任何剩余的字符序列 (.*
),直到行尾 ($
)。 ([^=]+)
和 (.*)
两边的括号确保捕获的子字符串保存在特殊的 Bash 数组变量 ${BASH_REMATCH[@]}
中,从索引 1 开始。
- 为简单起见,没有尝试预先验证变量名称,这意味着
printf -v
命令稍后可能会失败。
我有一个脚本可以将变量存储在 .txt 文件中供以后使用。我想从文件中安全地检索这些变量。
我现在是如何设置的:
Settings.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
命令,但它也有同样的问题。那么,如何安全的实现同样的功能呢?
您可以在采购前检查变量赋值格式:
#!/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
.
如果一行未通过测试,它 returns 来自带有错误代码和 echo "Break"
的函数,否则 Settings.txt
是来源。
注意:您可以在变量名称和值中使用授权字符来完成字符范围 [a-zA-Z0-9_]
和 [a-zA-Z0-9]
。
如果您不想单独执行验证和变量创建步骤:
更新,基于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 变量赋值而失败,但安全地 失败该行永远不会被评估为 command.请注意,这些值被读取为 文字 ,与文件中定义的完全相同(删除尾随空格除外)。
如果您还想支持单引号或双引号值,请改用以下
declare
命令:
declare -- "$(xargs -I {} printf %s {} <<<"$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 {} <<<"${BASH_REMATCH[2]}")"
但请注意,嵌入、转义引号的引用字符串不受支持。
- 如果有单引号或双引号的值,并且您想删除引号,请改用以下
应该可以安全使用,因为
printf -v
用于创建变量 - shell 不直接作为赋值语句的来源,这是可能发生注入的地方.简单地跳过未识别为变量赋值的行。
正则表达式
^([^=]+)=(.*)$
匹配任何以 (^
) 开头的至少 1 (+
) 个字符而不是=
([^=]
),紧接着是=
,接着是任何剩余的字符序列 (.*
),直到行尾 ($
)。([^=]+)
和(.*)
两边的括号确保捕获的子字符串保存在特殊的 Bash 数组变量${BASH_REMATCH[@]}
中,从索引 1 开始。- 为简单起见,没有尝试预先验证变量名称,这意味着
printf -v
命令稍后可能会失败。
- 为简单起见,没有尝试预先验证变量名称,这意味着