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 <<<"$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 不会直接获取赋值语句,而这正是可能发生注入的地方。
简单地跳过未被识别为变量赋值的行。
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 获取数据?