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

时间:2016-04-23 13:37:39

标签: bash parsing variable-assignment

我有一个脚本,可以将变量存储在.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命令,但也有同样的问题。那么,如何安全地实现相同的功能呢?

2 个答案:

答案 0 :(得分:3)

如果您不想要单独的步骤进行验证和变量创建:

更新,基于package com.example.enan.checkinternetconnection; public class MainActivity extends AppCompatActivity { private static final String TAG = ""; private static final String LOG_TAG = ""; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); } public boolean hasActiveInternetConnection(Context context) { if (isNetworkAvailable(context)) { try { HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection()); urlc.setRequestProperty("User-Agent", "Test"); urlc.setRequestProperty("Connection", "close"); urlc.setConnectTimeout(1500); urlc.connect(); return (urlc.getResponseCode() == 200); } catch (IOException e) { Log.e(LOG_TAG, "Error checking internet connection", e); } } else { Log.d(LOG_TAG, "No network available!"); } return false; } public boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null; } } :更安全的更简单方法是使用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 命令失败,输入行不是有效的shell变量赋值,但安全失败,因为该行永远不会被评估为命令

  • 请注意,这些值将被视为 literals ,与文件中定义的完全相同(删除尾随空格除外)。

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

原始答案,基于xargs

printf -v
  • 请注意,这些值将被读取为 literals ,与文件中定义的完全相同(删除尾随空格除外)。

    • 如果存在单引号或双引号值并且您要删除引号,请改用以下#!/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]}")"用于创建变量 - shell不会直接获取赋值语句,这是注入可能发生的地方。

    < / LI>
  • 简单地跳过未被识别为变量赋值的行。

  • 正则表达式printf -v匹配以(^([^=]+)=(.*)$)开头的任何行,除了^+)以外的至少1(=)个字符,直接跟[^=],后跟任意剩余的字符序列(=)到行尾(.*)。 $([^=]+)周围的括号可确保捕获的子字符串保存在特殊的Bash数组变量(.*)中,从索引1开始。

    • 为简单起见,没有尝试预先验证变量名称,这意味着${BASH_REMATCH[@]}命令可能会在以后失败。

答案 1 :(得分:1)

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

#!/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来源。

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