Что вас ожидает в этом руководстве
Безопасное управление конфиденциальными данными и настройками приложений может показаться сложной задачей для начинающих разработчиков. В этой статье мы разберем важные аспекты работы с переменными окружения, которые сделают вашу разработку более безопасной и эффективной.
Важно отметить, что рассмотренное решение не является лучшим способом для хранения секретных данных, однако, важность понимания принципов работы с переменными окружения сложно переоценить.
Начнем!
Что такое переменные окружения?
Переменные окружения — это значения, которые используются в различных командах и программных сценариях, выполняемых в операционной системе. Принципиально они работают точно так же, как переменные в языках программирования. Они представляют знакомые нам пары «ключ-значение» и используются для хранения параметров, настроек приложений, хранения ключей и других информационных данных.
Первый взгляд и практика
Давайте узнаем, какие переменные окружения уже есть в вашей операционной системе.
Примеры команд представлены для командной строки Windows и оболочки Unix (подобных) операционных систем отдельно.
CMD:
set
Bash:
printenv
Таким образом, вы увидите список переменных, которые уже существуют в окружении.
Взгляните внимательно на переменную окружения с именем PATH. Она имеет очень большое значение и содержит пути к директориям, содержащим исполняемые файлы программ. Когда мы обращаемся к различному программному обеспечению, используя его имя:
node main.js
Исполняемый файл с именем node (или node.exe для Windows) должен находиться в директориях, перечисленных в переменной окружения PATH. Если найти его не удастся, вы получите сообщение об ошибке:
command not found: node
или
"node" is not recognized as an internal or external command, operable program or batch file.
Если node.js уже установлен в операционной системе, но вы хотите ознакомиться с сообщением об ошибке из примера, используйте вместо node другой набор символов, который не будет соответствовать ни одному исполняемому файлу в директориях, перечисленных в PATH. Например, foo, bar или вовсе abracadabra123.
Если у вас уже установлена требуемая программа, но ее расположение отсутствует в переменной PATH, можно использовать полный путь к файлу в команде:
CMD:
"C:\Program Files\nodejs\node.exe" main.js
Bash:
# пример для nvm /root/.nvm/versions/node/v16.18.0/bin/node main.js
Напротив, если программа установлена и место ее расположения присутствует в переменной PATH, вы сможете узнать ее точное расположение следующим образом:
CMD:
where node
Bash:
which node
Создание, использование и удаление переменных окружения
Попробуем создать новую переменную:
CMD:
# Переменная доступна только для текущего сеанса командной строки set MSG=hello # Необходим перезапуск сессии командной строки # Такая переменная останется доступной даже после перезапуска операционной системы setx MSG hello /M /D
Bash:
# Переменная доступна только для текущего сеанса терминала: MSG=hello # Необходим перезапуск сессии терминала, # но переменная доступна для всех дочерних процессов export MSG=hello # Чтобы сделать переменную доступной после перезагрузки операционной системы, # необходимо добавить ее объявление в соответствующий файл конфигурации вашей оболочки # .bashrc для Bash echo 'export MSG=hello' >> ~/.bashrc source ~/.bashrc # .zshrc для Zsh echo 'export MSG=hello' >> ~/.zshrc source ~/.zshrc
Обратите внимание: так как переменные окружения являются константами, то общепринято писать их названия в верхнем регистре.
Пробуем использовать новую переменную:
CMD:
echo %MSG%
Bash:
echo $MSG
Теперь удаляем переменную:
CMD:
set MSG= setx MSG ''
Bash:
unset MSG # Также не забывайте удалить объявление переменной из файла # конфигурации оболочки, если она была объявлена в нем. В противном # случае, после перезапуска операционной системы переменная снова # будет создана
Конфиденциальные данные в приложении
Наше программное обеспечение использует различного рода конфиденциальную информацию для полноценной работы. Такой информацией может оказаться API/SSH ключ, пароль для подключения к базе данных, ssl-сертификат или нечто похожее. Если мы расположим эти данные внутри нашего публичного хранилища исходного кода, произойдет их компрометация. Даже в случае, если хранилище приватное, расположение секретной информации означает, что все его коллабораторы или пользователи получают доступ к закрытой информации.
Однако мы можем избежать таких рисков, переместив секретную информацию за пределы хранилища и заменив ее ссылками на место, где она безопасно хранится. Такой подход обеспечивает высокий уровень безопасности, поскольку каждый пользователь будет иметь доступ только к собственному месту хранения конфиденциальных данных. Например, каждый пользователь может иметь свой пароль к локальной базе данных, и его ссылка будет указывать именно на личное хранилище с этим паролем.
Использование внешних хранилищ для секретных данных позволяет нам эффективно управлять доступом к этой информации и предотвратить несанкционированный доступ. Кроме того, это делает сотрудничество в команде более безопасным. Этот подход также облегчает ротацию ключей и паролей без необходимости изменения кода приложения, что повышает уровень защиты и снижает риски для безопасности данных.
Хранение секретных данных в переменных окружения
Рассмотрим в качестве хранилища секретных данных переменные окружения.
Давайте представим, что нашему приложению необходимо подключение к базе данных. В этом случае мы создадим несколько переменных среды, разместив в них необходимую информацию на сервере:
Сервер
echo 'export DB_HOST=srv_db_host' >> ~/.bashrc echo 'export DB_PORT=srv_db_port' >> ~/.bashrc echo 'export DB_NAME=srv_db_name' >> ~/.bashrc echo 'export DB_USER=srv_db_user' >> ~/.bashrc echo 'export DB_PASSWORD=srv_db_password' >> ~/.bashrc source ~/.bashrc
Рабочее место разработчика
echo 'export DB_HOST=local_db_host' >> ~/.bashrc echo 'export DB_PORT=local_db_port' >> ~/.bashrc echo 'export DB_NAME=local_db_name' >> ~/.bashrc echo 'export DB_USER=local_db_user' >> ~/.bashrc echo 'export DB_PASSWORD=local_db_password' >> ~/.bashrc source ~/.bashrc
Внимание! Располагать переменные окружения в файле bashrc без ограничения доступа к нему посторонним лицам неприемлемо, информация предоставлена только в демонстративных целях!
Теперь мы можем получить доступ к этим переменным внутри приложения:
Node.js
/* process.env является глобальной переменной внутри node.jsприложения. Она хранит в себе состояние всех переменных окружения ОС на момент запуска приложения. */ const dbHost = process.env.DB_HOST; const dbPort = process.env.DB_PORT; const dbName = process.env.DB_NAME; const dbUser = process.env.DB_USER; const dbPassword = process.env.DB_PASSWORD;
Python
''' В языке программирования python доступ к переменным окружения возможно получить с использованием модуля OS. ''' import os db_host = os.environ['DB_HOST'] db_port = os.environ['DB_PORT'] db_name = os.environ['DB_NAME'] db_user = os.environ['DB_USER'] db_password = os.environ['DB_PASSWORD']
Golang
/* в языке программирования Go доступ к переменным окружения осуществляется c помощью пакета OS. */ package main import ( "os" ) func main() { dbHost := os.Getenv("DB_HOST") dbPort := os.Getenv("DB_PORT") dbName := os.Getenv("DB_NAME") dbUser := os.Getenv("DB_USER") dbPassword := os.Getenv("DB_PASSWORD") }
Использование файла .env для хранения переменных
Ранее мы рассмотрели, каким образом конфиденциальная информация может напрямую сохраняться в окружении операционной системы. Такой способ имеет некоторые недостатки. Эти переменные доступны не только для приложения.
Также есть некоторое неудобство их администрирования: они смешаны с переменными окружения, которые не нужны нашему приложению. Процесс замены значений переменных, в свою очередь, тоже не очень удобен.
Можем ли мы отделить необходимые переменные от остальных, сделать доступными только для приложения и управлять их состоянием удобным способом?
Да, мы можем воспользоваться практикой, ставшей золотым стандартом для решения этих проблем. Она заключается в использовании специальных библиотек, позволяющих при запуске приложения в его область видимости добавить ряд переменных, которые останутся недоступны другим процессам в операционной системе. Такие библиотеки присутствуют во всех популярных стеках разработки и используют в качестве хранилища специальный файл, оформленных строго определенным образом. Имя этого файла — .env (от англ. «environment»).
Синтаксис файла .env
Файл .env — это обычный текстовый файл без расширения с именем .env, каждая строка которого представляет собой пару ключ=значение. Вот пример записей переменных в такой файл:
DB_HOST=local_db_host DB_PORT=local_db_port DB_NAME=local_db_name DB_USER=local_db_user DB_PASSWORD=local_db_password
Давайте подключим этот файл в наше приложение, используя библиотеки, соответствующие необходимому стеку разработки:
Node.js:
/* Установим библиотеку dotenv при помощи пакетного менеджера: npm install dotenv или yarn add dotenv */ const dotenv = require('dotenv'); dotenv.config(); const dbHost = process.env.DB_HOST; const dbPort = process.env.DB_PORT; const dbName = process.env.DB_NAME; const dbUser = process.env.DB_USER; const dbPassword = process.env.DB_PASSWORD; /* Объявленные переменные содержат соответствующие значения из .env файла. Node.js версии 20 предоставляет экспериментальную поддержку .env файлов без использования библиотеки "dotenv": node --env-file=.env app.js */
Python:
# Установим пакет python-dotenv с помощью pip # pip install python-dotenv import os from dotenv import load_dotenv load_dotenv() db_host = os.getenv("DB_HOST") db_port = os.getenv("DB_PORT") db_name = os.getenv("DB_NAME") db_user = os.getenv("DB_USER") db_password = os.getenv("DB_PASSWORD") # Объявленные переменные содержат значения из .env файла и могут быть использованы
Golang:
/* Установим пакет github.com/joho/godotenv с помощью go get go get github.com/joho/godotenv */ package main import ( "github.com/joho/godotenv" "os" ) func main() { err := godotenv.Load() if err != nil { log.Fatalf("Error loading .env file") } dbHost := os.Getenv("DB_HOST") dbPort := os.Getenv("DB_PORT") dbName := os.Getenv("DB_NAME") dbUser := os.Getenv("DB_USER") dbPassword := os.Getenv("DB_PASSWORD") // Объявленные переменные содержат значения из .env файла и могут быть использованы }
ВНИМАНИЕ! Одна из важнейших целей использования .env в проекте — это отсутствие конфиденциальных данных в репозитории. Не забывайте позаботиться о добавлении .env в .gitignore, если вы используете git как систему контроля версий. Если используете альтернативную систему контроля версий, исключите .env в соответствии с технической документацией.
Разместите в .gitignore:
# Excluding the .env file.env
Такой подход хранения конфиденциальной информации можно наблюдать в большинстве крупных фреймворков или библиотек.
Laravel — один из наиболее популярных фреймворков для разработки веб-приложений на PHP. Файл
.envв Laravel используется для хранения настроек базы данных, настроек приложения, секретных ключей, параметров электронной почты и других конфигурационных параметров. Laravel автоматически загружает переменные окружения из файла.envпри запуске приложения.Django — это фреймворк для разработки веб-приложений на Python. В проектах Django также распространено использование файла
.envдля хранения настроек базы данных, секретных ключей, настроек электронной почты и других конфигурационных параметров. Для загрузки переменных окружения из файла.envв Django обычно используется библиотека python-dotenv.Ruby on Rails — фреймворк для разработки веб-приложений на Ruby. В проектах Ruby on Rails также популярно использование файла
.envдля хранения конфигурационных параметров, включая параметры базы данных, ключи API и другие настройки. Для загрузки переменных окружения из файла.envв Ruby on Rails обычно используется библиотека dotenv-rails.
На практике многие разработчики используют файл .env в своих проектах, чтобы легко настраивать и хранить параметры окружения и секретные данные без необходимости отображения их в коде.
.env.example
Правилами хорошего тона считается создание файла с именем .env.example, который должен хранить список необходимых для заполнения переменных. Этот файл используется для документирования и предоставления возможного примера заполнения значений переменных окружения. Обратите внимание, он может находиться в репозитории, но при этом не должен содержать настоящей конфиденциальной информации.
Заключение
В данной статье были рассмотрены важные аспекты обработки конфиденциальной информации в приложениях. Мы познакомились с наиболее распространенным способом сокрытия конфиденциальной информации из репозиториев программного обеспечения.
Давайте помнить, что безопасное обращение с конфиденциальными данными — это важный аспект разработки программного обеспечения. Использование файла .env в сочетании с грамотно организованными переменными окружения помогает обеспечить защиту информации и упрощает настройку приложений для различных окружений.
Спасибо за внимание!
