0Pricing
Linux Command Line & Bash Scripting Mastery · Урок

Безопасная работа с секретами и чистота окружения

Не допускайте попадания учётных данных в списки процессов и журналы, используя stdin, файлы и очищенное окружение

«Безопасная работа с секретами и чистота окружения» — бесплатный урок Linux Command Line & Bash Scripting Mastery на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Linux Command Line & Bash Scripting Mastery, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.

Почему важна гигиена секретов

Секреты — ключи API, пароли и токены — самые чувствительные данные в любой системе. Неправильное обращение с ними в скриптах Bash — одна из наиболее распространённых и опасных ошибок безопасности.

  • Списки процессов: аргументы, переданные командам, отображаются в ps aux, /proc/<pid>/cmdline и системных журналах аудита — их видят все пользователи узла.
  • История оболочки: команды, введённые интерактивно (а иногда и скрипты), записываются в ~/.bash_history.
  • Файлы журналов: трассировки set -x, журналы приложений и вывод CI/CD могут содержать значения переменных.
  • Утечка через окружение: дочерние процессы наследуют всё окружение родительского процесса, включая экспортированные секреты.

В усиленном скрипте с секретами обращаются как с радиоактивным материалом: сводят время раскрытия к минимуму, ограничивают область воздействия и очищают всё перед выводом.

Поверхность атаки через список процессов

Когда Вы передаёте секрет как аргумент командной строки, любой пользователь системы может немедленно прочитать его с помощью ps. Это не теоретическая угроза: ею регулярно пользуются в средах общего хостинга и контейнерах.

Приведённый ниже фрагмент демонстрирует проблему и её исправление рядом.

#!/usr/bin/env bash
# DANGEROUS: password visible in 'ps aux' output
# curl -u "admin:SuperSecret123" https://api.example.com/data

# SAFE: pass credentials via stdin or a flag that reads from a file
# Many tools support reading secrets from stdin with '-' or dedicated flags:

# Option 1 — pipe the secret so it never appears in argv
echo 'SuperSecret123' | curl -u 'admin' --password-stdin \
  https://api.example.com/data 2>/dev/null || true

# Option 2 — write a temporary netrc and point curl at it
# (covered in a later scene)
echo 'Secret never touches the command line this way'

Чтение секретов из стандартного ввода

Самый безопасный интерактивный способ — считывать секрет во время выполнения с помощью read -rs. Флаг -s отключает отображение вводимых символов, а -r запрещает интерпретацию обратной косой черты.

Основные моменты:

  • Переменная никогда не экспортируется, поэтому дочерние процессы не могут увидеть её через /proc/<pid>/environ.
  • После использования немедленно выполните unset для переменной, чтобы сократить время её раскрытия.
  • Избегайте echo "$SECRET" — используйте printf '%s', чтобы завершающий перевод строки не испортил значение и чтобы оно оставалось невидимым в трассировках.
#!/usr/bin/env bash
set -euo pipefail

# Prompt on stderr so stdout stays clean for piping
read -rsp 'Enter API token: ' API_TOKEN <&2
printf '\n' >&2

# Use the secret — printf keeps it out of argv
response=$(printf '%s' "$API_TOKEN" | curl -sS -X POST \
  -H 'Content-Type: application/json' \
  --data-binary @- \
  https://httpbin.org/post 2>/dev/null) || true

echo "Request sent."

# Scrub immediately — unset removes it from shell memory
unset API_TOKEN

Секреты в файлах: права доступа и владение

Если секрет должен сохраняться на диске (например, ключ служебной учётной записи), права доступа к файлу — ваша главная защита.

  • Режим 0600 — только владелец может читать и изменять файл. Доступ для группы и остальных пользователей отсутствует.
  • Режим 0400 — владелец может только читать файл. Предпочтительно использовать этот режим для ключей, которые ни при каких обстоятельствах нельзя случайно перезаписать.
  • Храните файлы с секретами в специально предназначенном каталоге, например ~/.secrets/ или /run/secrets/ (последний во многих системах Linux представляет собой tmpfs, размещённый в RAM, и существует только до перезагрузки).
  • Никогда не помещайте файлы с секретами в каталог, отслеживаемый git, если у вас нет абсолютно надёжного файла .gitignore.
#!/usr/bin/env bash
set -euo pipefail

SECRETS_DIR="${HOME}/.secrets"
mkdir -p "$SECRETS_DIR"
chmod 700 "$SECRETS_DIR"   # directory: only owner can list contents

KEY_FILE="${SECRETS_DIR}/api_token"

# Write secret — atomically restrict permissions before writing content
install -m 0600 /dev/null "$KEY_FILE"
printf '%s' 'my-super-secret-token' > "$KEY_FILE"

echo "Permissions:"
ls -la "$KEY_FILE"

# Read back safely — no subshell, no echo
API_TOKEN=$(< "$KEY_FILE")
echo "Token length: ${#API_TOKEN} chars (value not printed)"
unset API_TOKEN

Использование файла .netrc с curl

curl поддерживает файл ~/.netrc (или произвольный путь, указанный через --netrc-file), в котором имена узлов сопоставляются с учётными данными. Благодаря этому данные аутентификации полностью исключаются из командной строки и тела скрипта.

Формат файла прост:

machine api.example.com
  login admin
  password s3cr3t

Рекомендации:

  • Всегда устанавливайте права chmod 0600 ~/.netrc — в некоторых системах curl отказывается использовать файл, доступный для чтения всем пользователям.
  • Используйте --netrc-file /run/secrets/netrc, чтобы указать на секрет в tmpfs или секрет, переданный контейнеру.
  • Удаляйте временные файлы netrc с помощью trap при наступлении EXIT.
#!/usr/bin/env bash
set -euo pipefail

TMP_NETRC=$(mktemp)
chmod 0600 "$TMP_NETRC"

# Trap ensures cleanup even on error or signal
trap 'rm -f "$TMP_NETRC"' EXIT

# Write credentials to the temp netrc
cat > "$TMP_NETRC" <<'EOF'
machine httpbin.org
  login myuser
  password mypassword
EOF

curl -fsS --netrc-file "$TMP_NETRC" \
  https://httpbin.org/basic-auth/myuser/mypassword \
  -o /dev/null -w 'HTTP %{http_code}\n' || true

# trap fires here: $TMP_NETRC is deleted
echo 'Temp netrc cleaned up by trap.'

Безопасная работа с переменными окружения

Переменные окружения — популярный способ передавать секреты в скрипты (приложения по методологии 12 факторов, конвейеры CI/CD). Однако они передаются каждому дочернему процессу и отображаются в /proc/<pid>/environ на протяжении всего времени жизни процесса.

Защитные приёмы:

  • Сразу сохраните секрет в локальной переменной и снимите переменную окружения, чтобы дочерние процессы не могли её унаследовать.
  • Передавайте секреты только нужным командам с помощью env -i или встроенного присваивания, а не через полностью унаследованное окружение.
  • Никогда не используйте export для переменной с секретом — по возможности применяйте только присваивание (без export).
#!/usr/bin/env bash
set -euo pipefail

# Simulate a secret arriving via environment (e.g., from CI system)
export DB_PASSWORD='hunter2'   # set by CI — we did not choose this

# Capture locally, then strip from environment immediately
db_password="$DB_PASSWORD"
unset DB_PASSWORD

# Verify the env var is gone before spawning any child process
if printenv DB_PASSWORD 2>/dev/null; then
  echo 'ERROR: DB_PASSWORD still in environment!' >&2
  exit 1
fi

echo 'Secret captured and env var scrubbed.'
echo "Password length: ${#db_password}"
unset db_password

Как не допустить появления секретов в трассировке set -x

set -x (трассировка xtrace) незаменима при отладке, но выводит в stderr значение каждой раскрываемой переменной — в том числе секретов. Такие трассировки часто оказываются в журналах CI или syslog.

Стратегии защиты секретов при сохранении полезной трассировки:

  • Временно отключайте трассировку вокруг чувствительных операций с помощью { set +x; } 2>/dev/null.
  • После этого включайте её снова с помощью set -x.
  • Перенаправляйте вывод xtrace в отдельный файловый дескриптор, связанный с защищённым файлом журнала, а не с общедоступным потоком журналирования.
#!/usr/bin/env bash
set -euo pipefail
set -x   # tracing ON — safe for non-sensitive sections

echo 'Building application...'
SRC_DIR='/tmp/build'
mkdir -p "$SRC_DIR"

# Disable xtrace around secret handling (suppress the set +x line itself)
{ set +x; } 2>/dev/null

read -rsp 'Token (hidden from trace): ' SECRET_TOKEN <&2
printf '\n' >&2
token_len=${#SECRET_TOKEN}
unset SECRET_TOKEN

set -x  # tracing back ON

echo "Token captured (length=$token_len). Continuing build..."
ls "$SRC_DIR"

Удаление секретов из файлов журналов

Даже при осторожной работе секреты иногда попадают в журналы — особенно в подробных или устаревших скриптах. Функция-обёртка для журналирования, которая маскирует известные шаблоны, создаёт дополнительный уровень защиты.

В этом приёме для всех записей журнала используется замена на основе регулярных выражений. Это защита на крайний случай, а не замена описанным ранее правилам безопасной работы.

#!/usr/bin/env bash
set -euo pipefail

# A logging function that scrubs common secret patterns before writing
log() {
  local line
  # Replace anything that looks like key=VALUE or password=VALUE
  line=$(printf '%s\n' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***REDACTED***/gi')
  printf '[%s] %s\n' "$(date -u '+%T')" "$line"
}

# Usage:
log 'Connecting to database with password=hunter2'
log 'Loaded API token=sk-abc123xyz secret'
log 'Build step completed successfully'   # unchanged

Изолированные окружения с env -i

env -i запускает команду с полностью пустым окружением, не позволяя унаследованным переменным — включая случайно переданные секреты — попасть в дочерний процесс. Затем вы явно передаёте только необходимое.

Это особенно полезно при запуске ненадёжных скриптов, средств сборки или сторонних утилит, которые могут похитить данные окружения.

#!/usr/bin/env bash
set -euo pipefail

# Polluted parent environment (simulating a CI runner)
export AWS_SECRET_ACCESS_KEY='AKIAIOSFODNN7EXAMPLE'
export GITHUB_TOKEN='ghp_faketoken123'
export HOME="$HOME"
export PATH="$PATH"

echo '--- Child sees full environment:'
env | grep -E 'AWS|GITHUB' | head -5

echo '--- Sanitised child (env -i) sees nothing secret:'
env -i HOME="$HOME" PATH="$PATH" TERM="${TERM:-dumb}" \
  bash -c 'env | grep -E "AWS|GITHUB" || echo "No secrets visible"'

unset AWS_SECRET_ACCESS_KEY GITHUB_TOKEN

Временные файлы с секретами в tmpfs

tmpfs — это файловая система, размещённая в RAM. Записанные туда файлы никогда не сбрасываются на диск, благодаря чему исключается риск сохранения секретов в разделе подкачки, дисковом кэше или снимках.

  • В Linux /dev/shm и /run/user/<uid> обычно являются точками монтирования tmpfs.
  • Всегда сочетайте использование tmpfs с trap EXIT, чтобы удалять файлы после завершения скрипта.
  • В контейнерах (Docker, Kubernetes) секреты можно напрямую монтировать в /run/secrets как тома tmpfs.
#!/usr/bin/env bash
set -euo pipefail

# Prefer /run/user/$UID (user-owned tmpfs) or /dev/shm (world-readable dir!)
if [[ -d "/run/user/$UID" ]]; then
  TMPFS_DIR="/run/user/$UID"
elif [[ -d '/dev/shm' ]]; then
  TMPFS_DIR='/dev/shm'
else
  # Fallback: warn that disk will be used
  echo 'WARNING: No tmpfs available; using /tmp (disk-backed)' >&2
  TMPFS_DIR='/tmp'
fi

SECRET_FILE=$(mktemp "${TMPFS_DIR}/secret.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'shred -u "$SECRET_FILE" 2>/dev/null || rm -f "$SECRET_FILE"' EXIT

printf '%s' 'my-runtime-token' > "$SECRET_FILE"
echo "Secret stored in: $SECRET_FILE"
df -T "$SECRET_FILE" | awk 'NR==2 {print "Filesystem type:", $2}'

# Use the secret...
token=$(< "$SECRET_FILE")
echo "Token length: ${#token}"
unset token
# trap fires on exit: file shredded

Собираем всё вместе: усиленный скрипт развёртывания

Следующий скрипт объединяет все приёмы из этого урока в реалистичный вспомогательный скрипт развёртывания. Обратите внимание, как каждый уровень защиты усиливает остальные:

  • Чтение из stdin с -s — без отображения ввода в терминале
  • Файл с секретом в tmpfs с очисткой через trap
  • Очистка окружения — секрет удаляется до запуска любого дочернего процесса
  • Защита от xtrace — трассировка приостанавливается вокруг чувствительного кода
  • Маскирование в журнале — регулярное выражение-защита применяется перед записью в журнал
#!/usr/bin/env bash
set -euo pipefail

### 1. Redacting logger
log() {
  local msg
  msg=$(printf '%s' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***/gi')
  printf '[%s] %s\n' "$(date -u +%T)" "$msg"
}

### 2. tmpfs secret store
TMPFS_DIR="${XDG_RUNTIME_DIR:-/tmp}"
SECRET_FILE=$(mktemp "${TMPFS_DIR}/deploy_token.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'rm -f "$SECRET_FILE"; log "Secret file cleaned up."' EXIT

### 3. Read secret without trace
{ set +x; } 2>/dev/null
read -rsp 'Deploy token: ' _tok <&2; printf '\n' >&2
printf '%s' "$_tok" > "$SECRET_FILE"
unset _tok
set -x

### 4. Scrub inherited env vars before subprocess
unset DEPLOY_TOKEN 2>/dev/null || true

log 'Starting deployment...'
# Simulate deploy using secret from file (token never in argv)
# curl -H "Authorization: Bearer $(< $SECRET_FILE)" https://api.example.com/deploy
log 'Deployment complete. token=hidden_by_redactor'

echo 'Done.'

Проверка знаний: раскрытие секретов через список процессов

Проверьте, насколько хорошо вы понимаете, как секреты раскрываются через списки процессов и как этого не допустить.

Итоги урока: безопасная работа с секретами

Вы завершили урок Безопасная работа с секретами и окружением. Ниже приведена краткая памятка обо всём изученном:

  • Списки процессов: Никогда не передавайте секреты как аргументы командной строки — они отображаются в ps aux и /proc/<pid>/cmdline. Вместо этого используйте передачу через stdin или --netrc-file.
  • Чтение из stdin: Используйте read -rs, чтобы интерактивно получать секреты без отображения в терминале и без попадания в историю оболочки.
  • Права доступа к файлам: Для файлов с секретами необходимы права chmod 0600 (или 0400). Для атомарного создания используйте install -m 0600.
  • Файлы netrc: Передавайте учётные данные во временный файл, указанный через --netrc-file; удаляйте его с помощью trap EXIT.
  • Безопасная работа с окружением: Сразу после локального сохранения секрета используйте unset для переменных окружения с секретами; никогда не применяйте export без необходимости; для изоляции дочерних процессов используйте env -i.
  • Защита от xtrace: Оборачивайте чувствительный код в { set +x; } 2>/dev/null ... set -x, чтобы значения не попадали в отладочные трассировки.
  • Маскирование в журналах: Используйте журналирующую функцию на основе sed как защиту на крайний случай.
  • tmpfs: Храните секреты, необходимые во время работы, в /run/user/$UID или /dev/shm, чтобы они никогда не записывались на диск; удаляйте их при завершении.

Главный принцип — эшелонированная защита: ни одной меры недостаточно, но их совместное применение значительно усложняет утечку секретов.

Часто задаваемые вопросы

Урок «Безопасная работа с секретами и чистота окружения» бесплатный?

Да — полный текст урока «Безопасная работа с секретами и чистота окружения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Linux Command Line & Bash Scripting Mastery, подпишись на CoddyKit PRO. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.

Чему я научусь в уроке «Безопасная работа с секретами и чистота окружения»?

Не допускайте попадания учётных данных в списки процессов и журналы, используя stdin, файлы и очищенное окружение Ты практикуешь Linux Command Line & Bash Scripting Mastery с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Linux Command Line & Bash Scripting Mastery?

Предыдущий опыт не требуется. Linux Command Line & Bash Scripting Mastery на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Безопасная работа с секретами и чистота окружения»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Linux Command Line & Bash Scripting Mastery?

Да. Каждый урок Linux Command Line & Bash Scripting Mastery включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Предотвращение внедрения команд и аргументов
  2. Безопасная работа с секретами и чистота окружения
  3. Выполнение с минимальными привилегиями и дисциплина sudo
  4. Статический анализ и аудит с ShellCheck
← Назад к Linux Command Line & Bash Scripting Mastery