Пароль, переданный параметром команды, видит любой пользователь устройства
Пока команда выполняется, этот пароль видит каждый, у кого есть доступ к устройству — обычный, непривилегированный. Достаточно посмотреть список процессов.
Откуда он там виден
Аргументы, с которыми запущен процесс, лежат в /proc/<pid>/cmdline и доступны на чтение всем. Именно оттуда их берёт ps:
ps -eo pid,user,cmd | grep mysql
4821 deploy mysql -u admin -pSecretPass123 -e select 1
Никаких особых прав не нужно: любой пользователь системы видит команды всех остальных. На сервере с несколькими сотрудниками, на машине сборки, в контейнере с общим пространством процессов — везде.
Сюда же попадает всё, что передаётся параметром: токены в curl -H "Authorization: Bearer ...", ключи в вызовах утилит, строки подключения с паролем внутри.
Переменные окружения — лучше, но не решение
Второй распространённый способ:
DB_PASSWORD='super-secret-42' ./deploy.sh
В списке процессов пароля теперь нет. Зато окружение процесса лежит в /proc/<pid>/environ:
tr '\0' '\n' < /proc/4821/environ | grep -i password
DB_PASSWORD=super-secret-42
Разница с первым случаем важная: environ читается только владельцем процесса и суперпользователем, а cmdline — вообще всеми. То есть переменные окружения защищают от соседа по машине, но не от того, кто работает под тем же пользователем, и не от того, кто получил права root.
Отдельная беда — наследование. Дочерний процесс получает копию окружения родителя. Если ваш скрипт с паролем в переменной запускает что-то стороннее — сборщик, линтер, отправку метрик, — пароль уезжает и туда.
Где эти секреты всплывают потом
История команд. Пароль, набранный руками, ложится в ~/.bash_history и живёт там годами. Спасает пробел перед командой при включённом HISTCONTROL=ignorespace, но полагаться на это не стоит.
Логи систем сборки. Строка запуска попадает в вывод задачи. Маскирование секретов в системах CI работает по списку известных значений — если пароль собран из кусков или пришёл не из хранилища секретов, он выведется как есть.
Дампы и мониторинг. Инструменты, снимающие список процессов раз в минуту, аккуратно складывают ваши пароли в базу мониторинга, где к ним доступ у гораздо большего круга людей.
Сообщения об ошибках. Скрипт падает, окружение прикладывается к отчёту, отчёт уходит в систему трекинга.
Как передавать правильно
По возрастанию надёжности.
Через файл с правами 600. Утилиты обычно это умеют: у клиента MySQL это --defaults-extra-file, у многих других — --password-file. Файл читает только владелец, в списке процессов виден лишь путь.
Через стандартный ввод. Пароль передаётся в момент запуска и нигде не сохраняется:
printf '%s' "$DB_PASSWORD" | some-tool --password-stdin
Так работает большинство современных клиентов — именно потому, что параметром передавать нельзя.
Из менеджера секретов в момент использования. Секрет запрашивается перед самым вызовом и живёт только в памяти процесса — ни окружение, ни аргументы его не видят.
Без пароля вообще. Ключи вместо паролей, локальная аутентификация по учётной записи операционной системы, короткоживущие токены. Лучший секрет — тот, которого нет в скрипте.
Быстрая проверка своей инфраструктуры
Три команды, которые стоит выполнить прямо сейчас на любом рабочем сервере:
ps -eo cmd | grep -iE 'password|passwd|token|secret|api[_-]?key' | grep -v grep
grep -riE 'password=|token=' ~/.bash_history 2>/dev/null | head
grep -rlE 'password|secret' /etc/cron.d /etc/systemd/system 2>/dev/null | head
Первая покажет, что светится в процессах прямо сейчас. Вторая — что осталось в истории. Третья — где секреты прописаны в запускаемых по расписанию задачах, а это самое живучее место: такие файлы переживают и переезды, и смену команды.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.