We detected you are likely not from a Russian-speaking region. Would you like to switch to the international version of the site?

  Назад к списку статей

Статический анализ и hardening Bash‑скриптов в Termux: ShellCheck, shfmt и custom‑lint для предотвращения инъекций и race‑conditions

Профессиональный гид по hardening Bash‑скриптов в Termux: как применять ShellCheck, shfmt и custom‑lint правила для снижения рисков инъекций команд, уязвимостей с подстановками и race‑conditions.

Termux стал практичной средой для автоматизации задач на Android: от обслуживания устройств до локальной аналитики и интеграций с командной строкой. Однако Bash‑скрипты часто пишутся «быстро и по месту», а затем начинают выполняться в более сложных сценариях, где растут риски: подмена параметров, нежелательные расширения переменных, небезопасная работа с временными файлами и появление гонок (race‑conditions).

В этой статье разберём подход к статическому анализу и hardening Bash‑скриптов в Termux с опорой на три уровня защиты: ShellCheck (логические и безопасностные предупреждения), shfmt (единообразие и предотвращение «скрытых» ошибок форматирования) и custom‑lint правила (локальные, специфичные для ваших скриптов стандарты).

Почему статический анализ важнее «проверить вручную»

Многие уязвимости в Bash появляются не из‑за очевидных «дыр», а из‑за того, как интерпретируются переменные, кавычки, подстановки, редиректы и временные файлы. Статический анализ помогает обнаружить типовые ошибки до выполнения скрипта, а также ускоряет ревью кода: предупреждения становятся единым языком команды.

Отдельная проблема Bash — чувствительность к обработке строк. Любая команда, формируемая из строк, где присутствуют пользовательские входные данные, может привести к инъекциям команд. Параллельное выполнение (через cron, background‑задачи или вызовы из нескольких сессий Termux) способно проявить race‑conditions при создании/использовании файлов.

Базовый hardening Bash: настройки, которые стоит включить всегда

Прежде чем запускать линтеры, полезно установить «защитный режим» поведения shell. Это уменьшает вероятность того, что скрипт продолжит работу в неожиданном состоянии.

#!/data/data/com.termux/files/usr/bin/env bash
set -Eeuo pipefail
IFS=$'
\t'

# Рекомендуется явно включать безопасный поиск по PATH только если это нужно.
# export PATH="..."  # при необходимости

Ключевые моменты:

  • set -e — остановка при ошибках (осторожно, если у вас есть ожидаемые ошибки в проверках).
  • -u — запрет на неинициализированные переменные (снижает риск «подстановки пустоты»).
  • -o pipefail — корректная обработка ошибок в конвейерах.
  • IFS — уменьшает сюрпризы при разбиении строк.

Если скрипт должен быть совместим с sh, переносите стиль аккуратно: часть опций и синтаксиса может отличаться.

Установка и запуск ShellCheck в Termux

ShellCheck — это статический анализатор Bash/Shell, который указывает на проблемные места: кавычки, небезопасные паттерны, неиспользуемые переменные, спорные конструкции, ошибки логики.

Пример ориентировочной установки и запуска (команды могут отличаться по версии пакетов в вашем окружении):

pkg update
pkg install shellcheck

Далее анализируйте скрипт:

shellcheck ./script.sh

Для более «жёсткого» подхода можно собирать отчёт и фиксировать порог качества (в реальном процессе CI) — но даже простой прогон shellcheck регулярно даёт заметный эффект.

Какие категории проблем ShellCheck особенно полезны для hardening

Для задач безопасности и надёжности чаще всего встречаются следующие классы предупреждений:

  • Небезопасные кавычки — когда переменные используются без "...", строки могут расширяться неожиданно, включая разбиение на слова и globbing.
  • Инъекции команд — когда данные попадают в конструкцию выполнения команд (например, в eval или при формировании команд строкой).
  • Проблемы с IFS/word splitting — разделение на слова ломает логику и может привести к неверным аргументам.
  • Ошибки с временными файлами — когда файл создаётся без уникальности/проверок, появляется шанс race‑condition или перезаписи.
  • Параллельное выполнение и TOCTOU — проверка «существует/не существует» и затем «создать» между шагами может быть атакована гонкой или просто сломана конкурентностью.

Пример: типичная ошибка и исправление

Наглядно: если переменная подставляется в команду без кавычек, скрипт становится чувствительным к пробелам и спецсимволам.

# Плохо (пример)
file=$1
rm -f $file

# shellcheck обычно будет ругаться на word splitting/globbing.

# Хорошо
file="$1"
rm -f -- "$file"

Исправления:

  • Кавычки вокруг аргумента.
  • Флаг -- помогает не воспринимать путь как опции команды.

shfmt: единый стиль и меньше «скрытых» ошибок

shfmt форматирует POSIX‑like shell (включая значимую часть Bash‑стиля), приводя код к единообразию. На практике это помогает:

  • уменьшить вероятность пропуска скобок/переносов, которые иначе «теряются» в визуальном чтении;
  • сделать diff предсказуемым и облегчить ревью;
  • повысить дисциплину: сначала исправляем стиль, затем линтим.

Установка (ориентировочно):

pkg install shfmt

Форматирование скрипта:

shfmt -w ./script.sh

Если у вас в репозитории несколько файлов, удобно прогонять по glob‑паттерну (учитывайте оболочку и кавычки в вашем окружении).

Custom‑lint: как выстроить правила под «ваши» риски

ShellCheck — общий инструмент. Но в больших задачах полезны custom‑lint правила, которые учитывают ваш стиль и конкретные угрозы: например, запрет на создание временных файлов в «общих» местах или запрет на eval без исключений.

Пример простого custom‑lint на Bash (скрипт‑проверка, которая ищет запрещённые конструкции):

#!/data/data/com.termux/files/usr/bin/env bash
set -Eeuo pipefail

target="${1:-}"
if [ -z "$target" ] || [ ! -f "$target" ]; then
  echo "Usage: $0 path/to/script.sh" >&2
  exit 2
fi

# Пример: запрет на eval (как класс риска)
grep -nE '\beval\b' "$target" && {
  echo "Custom-lint: запрещено использовать eval. Обсудите альтернативу." >&2
  exit 1
}

# Пример: запрет на /tmp без уникальности (очень условно)
grep -nE '/tmp/' "$target" && {
  echo "Custom-lint: проверьте работу с /tmp на race-condition и коллизии." >&2
  exit 1
}

echo "Custom-lint: OK"

Важно: такие правила лучше делать точными и постепенно ужесточать. Иначе линтер начнёт «шуметь» и команда будет игнорировать предупреждения.

Предотвращение инъекций команд в Bash

Ключевые практики:

  • Не используйте eval для данных пользователя. Если требуется «динамика» — лучше применять маппинг: case, списки команд или массивы аргументов.
  • Кавычки для всех переменных, которые попадают в команды: cmd -- "$var".
  • Не конструируйте команды строкой. Предпочитайте вызов с аргументами через массивы: cmd -- "$arg1" "$arg2".
  • Валидация входа: если аргумент должен быть числом — проверяйте регулярным выражением или арифметикой.

Пример безопасного паттерна (без «склеивания строк»):

mode="${1:-}"
case "$mode" in
  fast|safe)
    ;;
  *)
    echo "Unknown mode: $mode" >&2
    exit 2
    ;;
esac

# Аргументы передаются как отдельные элементы
set -- "--mode" "$mode"
mytool "$@"

Предотвращение race‑conditions: временные файлы, TOCTOU и конкуренция

Race‑conditions в shell чаще всего проявляются при:

  • проверке [ -e file ], затем создании touch file — между действиями состояние может измениться;
  • работе с фиксированными путями во /tmp или в текущей директории;
  • использовании файлов без атомарности.

Практики:

  • Используйте уникальные временные имена (например, через mktemp).
  • Если нужно блокирование — применяйте file-lock подходы (в зависимости от требований вашего сценария).
  • Старайтесь избегать TOCTOU: «сначала проверь — потом сделай» заменяйте на «сделай и обработай результат».

Пример: корректная работа с временным файлом через mktemp:

tmpfile="$(mktemp -t termux-script-XXXXXX)"
trap 'rm -f -- "$tmpfile"' EXIT

# дальнейшая работа
printf '%s
' "payload" > "$tmpfile"

Подход с trap помогает избежать утечек временных файлов при ошибках.

Практический пайплайн: как встроить линтинг в ваш рабочий процесс Termux

Рекомендованный цикл:

  1. Форматирование: прогоняйте shfmt -w на исходниках, чтобы убрать визуальные расхождения.
  2. Статический анализ: запускайте shellcheck и фиксируйте критические предупреждения (особенно связанные с кавычками и инъекциями).
  3. Custom‑lint: проверяйте ваши «запреты» и соглашения (например, отсутствие eval, ограничения на работу с временными файлами).
  4. Проверка конкурентности: если скрипт потенциально запускается параллельно — проверьте коллизии имён и атомарность операций.

В результате код становится более предсказуемым, а риски — управляемыми.

Про тестирование: минимальный набор проверок перед релизом

Статический анализ не заменяет тесты, но дополняет их. Минимально полезно:

  • Проверить скрипт на «краевых» аргументах (пустые строки, пробелы, спецсимволы) — убедиться, что кавычки работают правильно.
  • Проверить сценарии параллельного запуска, если это реально в вашем контексте (хотя бы на уровне «двумя запускать — что происходит с временными файлами»).
  • Проверить обработку ошибок: что происходит при отказе команд, и не скрывается ли сбой.

Заключение

Hardening Bash‑скриптов в Termux — это дисциплина: правильный базовый режим shell (set -Eeuo pipefail и дисциплина кавычек), регулярный статический анализ через shellcheck, унификация стиля с shfmt и дополнение собственными custom‑lint правилами для ваших рисков (инъекции команд, race‑conditions, TOCTOU). Такой подход снижает вероятность инцидентов и делает автоматизацию надёжной.

Если вам нужно выстроить качественный процесс линтинга и безопасной разработки под ваши сценарии Termux, команда РыбинскЛАБ поможет с аудитом скриптов, настройкой пайплайна (ShellCheck/shfmt/custom‑lint) и внедрением практик hardening в ваш рабочий процесс.

* Текст статьи подготовлен и структурирован с использованием технологий искусственного интеллекта. Проверен и доработан перед публикацией.

Нужна помощь с настройкой Termux, Linux и серверов?

Я оказываю ИТ-услуги: настройка серверов, автоматизация, безопасность, помощь с Linux и инфраструктурой. Материалы сайта — только в ознакомительных и образовательных целях.

Связаться со мной
Поддержать проект