Shell-скрипт может содержать не только команды Bash, но и дополнительные данные:
- конфигурационные файлы;
- шаблоны;
- сертификаты и публичные ключи;
- архив с программой;
- статические ресурсы;
- вспомогательные исполняемые файлы;
- другой Shell-скрипт;
- зашифрованный контейнер.
Такой набор встроенных данных часто называют полезной нагрузкой, или
payload. Это не обязательно что-то вредоносное: полезной нагрузкой может быть архив приложения, конфигурация сервера или набор файлов для установки программы.В статье рассмотрим способы упаковки и защиты данных внутри
.sh-файла.---
## Содержание
1. Кодирование, упаковка и шифрование
2. Встраивание текста через Here Document
3. Встраивание бинарного файла через Base64
4. Создание самораспаковывающегося Shell-скрипта
5. Автоматическая сборка установщика
6. Использование Makeself
7. Защита Shell-скрипта с помощью SHC
8. Шифрование через age
9. Шифрование через OpenSSL
10. Проверка целостности и подлинности
11. Безопасная работа с временными файлами
12. Как исследовать неизвестный самораспаковывающийся скрипт
13. Что выбрать на практике
---
# 1. Кодирование, упаковка и шифрование
Сначала нужно различать несколько понятий.
## Кодирование
Кодирование изменяет представление данных, но не защищает их.
Пример:
Bash:
base64 program.bin > program.bin.b64
Восстановление:
Bash:
base64 -d program.bin.b64 > program.bin
Base64 удобно использовать для размещения бинарных данных внутри текстового файла, однако любой пользователь сможет их декодировать.
## Сжатие
Сжатие уменьшает размер данных:
Bash:
tar -czf payload.tar.gz payload/
Сжатие также немного усложняет беглый просмотр содержимого, но не является защитой.
## Обфускация
Обфускация делает код менее понятным:
Bash:
a="ec"
b="ho"
"$a$b" "Hello"
Результат:
Код:
Hello
Обфускация может затруднить чтение исходника, но не обеспечивает настоящей секретности.
## Шифрование
Шифрование защищает данные криптографически. Для расшифровки потребуется пароль или закрытый ключ.
Подходящие инструменты:
age;- GnuPG;
- OpenSSL;
- системы управления секретами;
- аппаратные криптографические токены.
Для новых проектов удобнее всего использовать
age. Это специализированная утилита шифрования файлов, поддерживающая пароли, собственные ключи и SSH-ключи.---
# 2. Встраивание текста через Here Document
Самый простой вариант — разместить текстовый файл непосредственно внутри скрипта.
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
cat > application.conf <<'CONFIG'
server {
listen = 127.0.0.1
port = 8080
debug = false
}
CONFIG
chmod 600 application.conf
echo "Конфигурация создана: application.conf"
Конструкция:
Bash:
<<'CONFIG'
означает, что переменные внутри блока не будут подставляться.
Например:
Bash:
cat <<'TEXT'
Пользователь: $USER
Каталог: $HOME
TEXT
выведет буквальный текст:
Код:
Пользователь: $USER
Каталог: $HOME
Если убрать кавычки:
Bash:
cat <<TEXT
Пользователь: $USER
Каталог: $HOME
TEXT
Bash подставит реальные значения переменных.
Here Document хорошо подходит для:
- конфигурационных файлов;
- systemd-юнитов;
- небольших HTML-страниц;
- шаблонов Nginx;
- SQL-запросов;
- публичных ключей;
- коротких вспомогательных скриптов.
---
# 3. Встраивание бинарного файла через Base64
Бинарный файл нельзя безопасно вставить в обычный текстовый блок. Сначала его можно преобразовать в Base64.
Допустим, есть файл:
Код:
helper.bin
Кодируем его:
Bash:
base64 helper.bin > helper.bin.b64
После этого содержимое
helper.bin.b64 можно разместить внутри Shell-скрипта:
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
OUTPUT="./helper.bin"
base64 -d > "$OUTPUT" <<'PAYLOAD'
f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAA...
PAYLOAD
chmod 700 "$OUTPUT"
echo "Файл восстановлен: $OUTPUT"
Здесь строка:
Bash:
base64 -d
декодирует встроенные данные и записывает результат в
helper.bin.Важно понимать: Base64 ничего не шифрует. Содержимое можно извлечь обычной командой.
---
# 4. Самораспаковывающийся Shell-скрипт
Более практичный вариант — разместить в начале файла исполняемый Shell-код, а после специального маркера добавить архив.
Структура файла будет выглядеть так:
Код:
#!/usr/bin/env bash
Shell-код установщика...
__ARCHIVE_BELOW__
H4sIAAAAAAAAA...
Скрипт:
1. определяет номер строки с маркером;
2. читает всё, что находится после него;
3. декодирует Base64;
4. проверяет контрольную сумму;
5. распаковывает архив.
## Пример распаковщика
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
readonly EXPECTED_SHA256="ВСТАВИТЬ_SHA256_АРХИВА"
SELF_PATH="$(readlink -f "$0" 2>/dev/null || printf '%s' "$0")"
DESTINATION="${1:-./extracted-payload}"
TEMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf -- "$TEMP_DIR"
}
trap cleanup EXIT INT TERM
ARCHIVE_LINE="$(
awk '/^__ARCHIVE_BELOW__$/ {
print NR + 1
exit
}' "$SELF_PATH"
)"
if [[ -z "$ARCHIVE_LINE" ]]; then
echo "Ошибка: встроенный архив не найден." >&2
exit 1
fi
ARCHIVE_PATH="$TEMP_DIR/payload.tar.gz"
tail -n +"$ARCHIVE_LINE" "$SELF_PATH" |
base64 -d > "$ARCHIVE_PATH"
ACTUAL_SHA256="$(
sha256sum "$ARCHIVE_PATH" |
awk '{print $1}'
)"
if [[ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]]; then
echo "Ошибка: контрольная сумма архива не совпадает." >&2
echo "Ожидалось: $EXPECTED_SHA256" >&2
echo "Получено: $ACTUAL_SHA256" >&2
exit 1
fi
mkdir -p -- "$DESTINATION"
tar -xzf "$ARCHIVE_PATH" \
-C "$DESTINATION" \
--no-same-owner \
--no-same-permissions
echo "Полезная нагрузка распакована в: $DESTINATION"
exit 0
__ARCHIVE_BELOW__
После маркера нужно добавить архив, преобразованный в Base64:
Bash:
base64 payload.tar.gz >> installer.sh
Затем:
Bash:
chmod 700 installer.sh
Запуск:
Bash:
./installer.sh
Или с указанием каталога:
Bash:
./installer.sh /opt/my-application
---
# 5. Автоматическая сборка установщика
Вручную подставлять контрольную сумму и добавлять архив неудобно. Лучше создать отдельный сборочный скрипт.
## Структура проекта
Код:
project/
├── build.sh
├── payload/
│ ├── install.sh
│ ├── application.conf
│ └── bin/
│ └── application
└── output/
## Скрипт
build.sh
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
readonly PROJECT_DIR="$(
cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &&
pwd
)"
readonly PAYLOAD_DIR="$PROJECT_DIR/payload"
readonly OUTPUT_DIR="$PROJECT_DIR/output"
readonly OUTPUT_FILE="$OUTPUT_DIR/installer.sh"
TEMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf -- "$TEMP_DIR"
}
trap cleanup EXIT INT TERM
if [[ ! -d "$PAYLOAD_DIR" ]]; then
echo "Ошибка: каталог payload не найден." >&2
exit 1
fi
mkdir -p -- "$OUTPUT_DIR"
ARCHIVE="$TEMP_DIR/payload.tar.gz"
tar -C "$PAYLOAD_DIR" -czf "$ARCHIVE" .
ARCHIVE_SHA256="$(
sha256sum "$ARCHIVE" |
awk '{print $1}'
)"
cat > "$OUTPUT_FILE" <<EOF
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
readonly EXPECTED_SHA256="$ARCHIVE_SHA256"
SELF_PATH="\$(readlink -f "\$0" 2>/dev/null || printf '%s' "\$0")"
DESTINATION="\${1:-./extracted-payload}"
TEMP_DIR="\$(mktemp -d)"
cleanup() {
rm -rf -- "\$TEMP_DIR"
}
trap cleanup EXIT INT TERM
ARCHIVE_LINE="\$(
awk '/^__ARCHIVE_BELOW__\$/ {
print NR + 1
exit
}' "\$SELF_PATH"
)"
if [[ -z "\$ARCHIVE_LINE" ]]; then
echo "Ошибка: встроенный архив не найден." >&2
exit 1
fi
ARCHIVE_PATH="\$TEMP_DIR/payload.tar.gz"
tail -n +"\$ARCHIVE_LINE" "\$SELF_PATH" |
base64 -d > "\$ARCHIVE_PATH"
ACTUAL_SHA256="\$(
sha256sum "\$ARCHIVE_PATH" |
awk '{print \$1}'
)"
if [[ "\$ACTUAL_SHA256" != "\$EXPECTED_SHA256" ]]; then
echo "Ошибка проверки контрольной суммы." >&2
exit 1
fi
mkdir -p -- "\$DESTINATION"
tar -xzf "\$ARCHIVE_PATH" \
-C "\$DESTINATION" \
--no-same-owner \
--no-same-permissions
echo "Файлы распакованы в: \$DESTINATION"
exit 0
__ARCHIVE_BELOW__
EOF
base64 "$ARCHIVE" >> "$OUTPUT_FILE"
chmod 700 "$OUTPUT_FILE"
echo "Установщик создан: $OUTPUT_FILE"
echo "SHA-256 архива: $ARCHIVE_SHA256"
Делаем скрипт исполняемым:
Bash:
chmod 700 build.sh
Собираем установщик:
Bash:
./build.sh
Результат:
Код:
output/installer.sh
## Почему установщик только распаковывает файлы
Безопаснее не запускать встроенный код автоматически.
Пользователь сможет сначала проверить содержимое:
Bash:
./installer.sh ./inspection
Затем:
Bash:
find ./inspection -type f -maxdepth 3 -print
После проверки установочный файл можно запустить вручную:
Bash:
sudo ./inspection/install.sh
Такой подход безопаснее, чем незаметное выполнение полезной нагрузки сразу после распаковки.
---
# 6. Использование Makeself
Необязательно создавать собственный формат установщика. Для этого существует утилита Makeself.
Код:
sudo apt update
sudo apt install makeself
Makeself создаёт самораспаковывающийся архив, который выглядит как Shell-скрипт и обычно имеет расширение
.run. В начало файла добавляется Shell-загрузчик, а после него располагается сжатый TAR-архив. Makeself также поддерживает встроенную проверку целостности.## Подготовка каталога
Код:
package/
├── install.sh
├── application.conf
└── application.bin
Файл
package/install.sh:
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
echo "Каталог распаковки: $(pwd)"
echo "Файлы установщика подготовлены."
# Здесь должна находиться прозрачная логика установки.
# Не рекомендуется запускать загруженный из сети код.
Разрешаем выполнение:
Bash:
chmod 700 package/install.sh
## Создание архива
Bash:
makeself \
--gzip \
./package \
./application-installer.run \
"Application Installer" \
./install.sh
Запуск:
Bash:
chmod 700 application-installer.run
./application-installer.run
## Распаковка без запуска установщика
Перед выполнением архив можно распаковать для проверки:
Bash:
./application-installer.run \
--noexec \
--target ./inspection
После этого следует изучить содержимое:
Bash:
find ./inspection -type f -print
И проверить Shell-код:
Bash:
bash -n ./inspection/install.sh
## Когда использовать Makeself
Makeself подходит для:
- автономных установщиков;
- доставки программы без пакетного менеджера;
- офлайн-установки;
- внутренних корпоративных инструментов;
- аварийных комплектов восстановления;
- программ, состоящих из нескольких файлов.
---
# 7. Защита Shell-скрипта с помощью SHC
Утилита SHC, или Shell Script Compiler, преобразует Shell-скрипт в исходный код на C, а затем компилирует его в исполняемый файл.
При этом SHC не превращает сценарий в полностью независимую нативную программу. Полученный файл по-прежнему зависит от интерпретатора, указанного в строке
shebang, например /bin/bash или /bin/sh.## Установка зависимостей
Bash:
sudo apt update
sudo apt install build-essential
Код:
sudo apt update
sudo apt install shc
После установки SHC компиляция выполняется так:
Bash:
shc -f application.sh -o application
Получатся файлы:
Код:
application
application.sh.x.c
Файл:
Код:
application
можно запускать как обычную программу:
Bash:
./application
Сгенерированный C-файл можно удалить:
Bash:
rm -f application.sh.x.c
## Что даёт SHC
SHC позволяет:
- скрыть исходный текст от беглого просмотра;
- распространять Shell-сценарий как исполняемый файл;
- уменьшить вероятность случайного изменения скрипта;
- усложнить копирование отдельных функций.
## Чего SHC не даёт
SHC не является надёжным хранилищем секретов.
Не следует помещать внутрь такого файла:
- постоянные API-ключи;
- пароли баз данных;
- закрытые SSH-ключи;
- мастер-пароли;
- токены облачных провайдеров;
- ключи расшифровки собственной полезной нагрузки.
Во время выполнения команды и данные должны попасть в память процесса. Поэтому специалист с доступом к системе потенциально сможет провести анализ исполняемого файла.
Правильнее считать SHC средством обфускации и упаковки, а не криптографической защиты.
---
# 8. Шифрование через age
age — удобный современный инструмент для шифрования файлов. В Debian 12 и Ubuntu 22.04 и новее он доступен через пакетный менеджер.Установка:
Bash:
sudo apt update
sudo apt install age
## Шифрование паролем
Сначала создаём архив:
Bash:
tar -czf payload.tar.gz payload/
Шифруем:
Bash:
age \
--passphrase \
--output payload.tar.gz.age \
payload.tar.gz
Утилита запросит пароль.
Расшифровка:
Bash:
age \
--decrypt \
--output payload.tar.gz \
payload.tar.gz.age
## Текстовый формат
Для размещения зашифрованного файла внутри Shell-скрипта удобно использовать ASCII Armor:
Bash:
age \
--passphrase \
--armor \
--output payload.age.txt \
payload.tar.gz
Получится текст примерно такого вида:
Код:
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3Yx...
-----END AGE ENCRYPTED FILE-----
Его можно добавить после маркера:
Код:
__ENCRYPTED_PAYLOAD_BELOW__
-----BEGIN AGE ENCRYPTED FILE-----
...
-----END AGE ENCRYPTED FILE-----
## Расшифровка встроенного контейнера
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
SELF_PATH="$(readlink -f "$0" 2>/dev/null || printf '%s' "$0")"
DESTINATION="${1:-./decrypted-payload}"
TEMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf -- "$TEMP_DIR"
}
trap cleanup EXIT INT TERM
PAYLOAD_LINE="$(
awk '/^__ENCRYPTED_PAYLOAD_BELOW__$/ {
print NR + 1
exit
}' "$SELF_PATH"
)"
if [[ -z "$PAYLOAD_LINE" ]]; then
echo "Ошибка: зашифрованная нагрузка не найдена." >&2
exit 1
fi
ENCRYPTED_FILE="$TEMP_DIR/payload.age"
ARCHIVE_FILE="$TEMP_DIR/payload.tar.gz"
tail -n +"$PAYLOAD_LINE" "$SELF_PATH" > "$ENCRYPTED_FILE"
age \
--decrypt \
--output "$ARCHIVE_FILE" \
"$ENCRYPTED_FILE"
mkdir -p -- "$DESTINATION"
tar -xzf "$ARCHIVE_FILE" \
-C "$DESTINATION" \
--no-same-owner \
--no-same-permissions
echo "Данные расшифрованы в: $DESTINATION"
exit 0
__ENCRYPTED_PAYLOAD_BELOW__
После маркера добавляется содержимое файла:
Bash:
cat payload.age.txt >> encrypted-installer.sh
chmod 700 encrypted-installer.sh
При запуске:
Bash:
./encrypted-installer.sh
age запросит пароль.## Шифрование публичным ключом
Пароль можно не использовать. Вместо этого создаётся пара ключей.
Генерация закрытого ключа:
Bash:
age-keygen -o identity.txt
Получение публичного ключа:
Bash:
age-keygen -y identity.txt > recipient.txt
Шифрование:
Bash:
age \
--recipients-file recipient.txt \
--output payload.tar.gz.age \
payload.tar.gz
Расшифровка:
Bash:
age \
--decrypt \
--identity identity.txt \
--output payload.tar.gz \
payload.tar.gz.age
Файл
recipient.txt можно передавать свободно. Файл identity.txt необходимо защищать:
Bash:
chmod 600 identity.txt
## Не встраивайте пароль в скрипт
Плохой вариант:
Bash:
PASSWORD="my-super-password"
age --decrypt \
--passphrase \
payload.age
И ещё хуже:
Bash:
echo "$PASSWORD" | some-decryption-command
Пароль может попасть:
- в исходный файл;
- в резервную копию;
- в историю Git;
- в журналы;
- в дамп памяти;
- в переменные окружения процесса;
- в систему мониторинга.
Лучше использовать:
- интерактивный ввод;
- закрытый ключ;
- системное хранилище секретов;
- HashiCorp Vault;
- systemd credentials;
- Docker secrets;
- Kubernetes Secrets с дополнительным шифрованием;
- аппаратный токен.
---
# 9. Шифрование через OpenSSL
OpenSSL также позволяет зашифровать файл с помощью пароля.
## Шифрование
Bash:
openssl enc \
-aes-256-cbc \
-salt \
-pbkdf2 \
-iter 200000 \
-in payload.tar.gz \
-out payload.tar.gz.enc
## Расшифровка
Bash:
openssl enc \
-d \
-aes-256-cbc \
-salt \
-pbkdf2 \
-iter 200000 \
-in payload.tar.gz.enc \
-out payload.tar.gz
Опция:
Код:
-pbkdf2
включает получение ключа из пароля через PBKDF2.
Опция:
Код:
-iter 200000
задаёт число итераций функции получения ключа.
Опция:
Код:
-salt
включает использование случайной соли. В OpenSSL соль используется по умолчанию, а документация отдельно предупреждает, что
-nosalt не следует применять, кроме специальных случаев совместимости и тестирования.## Текстовый вывод
Зашифрованный файл можно сразу преобразовать в Base64:
Bash:
openssl enc \
-aes-256-cbc \
-salt \
-pbkdf2 \
-iter 200000 \
-a \
-in payload.tar.gz \
-out payload.tar.gz.enc.txt
Расшифровка:
Bash:
openssl enc \
-d \
-aes-256-cbc \
-salt \
-pbkdf2 \
-iter 200000 \
-a \
-in payload.tar.gz.enc.txt \
-out payload.tar.gz
Для новых самостоятельных проектов
age обычно удобнее: его интерфейс сложнее использовать неправильно, а работа с получателями и закрытыми ключами предусмотрена изначально.---
# 10. Проверка целостности и подлинности
Шифрование, контрольная сумма и цифровая подпись решают разные задачи.
## SHA-256
Контрольная сумма обнаруживает случайное повреждение файла:
Bash:
sha256sum installer.sh
Создание файла с контрольной суммой:
Bash:
sha256sum installer.sh > installer.sh.sha256
Проверка:
Bash:
sha256sum -c installer.sh.sha256
Однако злоумышленник, который может заменить установщик, может одновременно заменить и файл с контрольной суммой.
## Цифровая подпись
Для проверки автора нужна цифровая подпись.
Например, с помощью GnuPG:
Bash:
gpg \
--armor \
--detach-sign \
installer.sh
Получится:
Код:
installer.sh.asc
Проверка:
Bash:
gpg \
--verify installer.sh.asc \
installer.sh
Подпись имеет смысл только тогда, когда пользователь заранее получил доверенный публичный ключ разработчика.
## Рекомендуемая схема распространения
Публикуются три файла:
Код:
installer.sh
installer.sh.sha256
installer.sh.asc
Пользователь выполняет:
Bash:
sha256sum -c installer.sh.sha256
gpg --verify installer.sh.asc installer.sh
И только после этого запускает:
Bash:
chmod 700 installer.sh
./installer.sh
---
# 11. Безопасная работа с временными файлами
При извлечении полезной нагрузки нельзя использовать предсказуемые пути вроде:
Bash:
/tmp/payload
Другой пользователь может заранее создать файл или символическую ссылку с таким именем.
Правильнее применять:
Bash:
TEMP_DIR="$(mktemp -d)"
Удаление временного каталога:
Bash:
cleanup() {
rm -rf -- "$TEMP_DIR"
}
trap cleanup EXIT INT TERM
Также полезно установить:
Bash:
umask 077
После этого создаваемые файлы будут по умолчанию доступны только текущему пользователю.
## Безопасный базовый шаблон
Bash:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
TEMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf -- "$TEMP_DIR"
}
trap cleanup EXIT INT TERM
echo "Временный каталог: $TEMP_DIR"
## Зачем нужен
set -Eeuo pipefail
Bash:
set -Eeuo pipefail
включает более строгий режим Bash:
-e— завершение при ошибке команды;-E— наследование обработчиков ошибок функциями;-u— ошибка при обращении к неопределённой переменной;pipefail— конвейер считается ошибочным, если завершилась с ошибкой любая его команда.
---
# 12. Как исследовать неизвестный самораспаковывающийся скрипт
Нельзя запускать неизвестный
.sh- или .run-файл только потому, что он выглядит как установщик.## Определение типа файла
Bash:
file unknown-installer.run
## Просмотр начала
Bash:
head -n 100 unknown-installer.run
## Поиск маркеров
Bash:
grep -nE \
'PAYLOAD|ARCHIVE|BEGIN|base64|openssl|age|tar|gzip' \
unknown-installer.run
## Проверка синтаксиса
Bash:
bash -n unknown-installer.run
Проверка синтаксиса не подтверждает безопасность, но позволяет обнаружить ошибки Bash.
## Статический анализ через ShellCheck
Bash:
shellcheck unknown-installer.run
## Поиск опасных конструкций
Bash:
grep -nE \
'curl|wget|eval|bash -c|sh -c|sudo|systemctl|crontab|chmod|chown|/etc/|/proc/|/dev/' \
unknown-installer.run
Наличие таких команд не всегда означает атаку, однако требует дополнительной проверки.
## Извлечение данных без выполнения
Если после маркера расположен Base64:
Bash:
LINE="$(
awk '/^__ARCHIVE_BELOW__$/ {
print NR + 1
exit
}' unknown-installer.run
)"
tail -n +"$LINE" unknown-installer.run |
base64 -d > extracted.tar.gz
Просмотр списка файлов без распаковки:
Bash:
tar -tzf extracted.tar.gz
Распаковка:
Bash:
mkdir inspection
tar -xzf extracted.tar.gz \
-C inspection \
--no-same-owner \
--no-same-permissions
После этого:
Bash:
find inspection -type f -print
## Не используйте
curl | bashОпасная конструкция:
Bash:
curl -fsSL https://example.org/install.sh | bash
Она сразу передаёт загруженный код интерпретатору.
Безопаснее:
Bash:
curl -fL \
-o install.sh \
https://example.org/install.sh
Затем:
Bash:
sha256sum install.sh
less install.sh
bash -n install.sh
shellcheck install.sh
И только после проверки:
Bash:
chmod 700 install.sh
./install.sh
---
# 13. Что выбрать на практике
## Для небольшого текстового файла
Используйте Here Document:
Bash:
cat > config.ini <<'EOF'
...
EOF
## Для одного бинарного файла
Используйте Base64:
Bash:
base64 file.bin
Но помните, что это кодирование, а не шифрование.
## Для нескольких файлов
Используйте:
Bash:
tar + gzip + Base64
или готовую утилиту Makeself.
## Для автономного Linux-установщика
Наиболее удобный вариант:
Код:
Makeself
Он уже реализует упаковку, распаковку, временный каталог и проверку целостности.
## Для защиты исходника от беглого просмотра
Можно использовать:
Код:
SHC
Но это обфускация, а не надёжное шифрование.
## Для конфиденциальных данных
Используйте:
Код:
age
Предпочтительно шифровать на публичный ключ получателя, а не хранить общий пароль внутри скрипта.
## Для подтверждения автора
Используйте цифровую подпись:
Код:
GnuPG, Minisign или другой инструмент подписи
## Рекомендуемая архитектура
Для серьёзного проекта можно использовать следующую схему:
Код:
Исходные файлы
│
▼
TAR-архив
│
▼
Шифрование через age
│
▼
Встраивание в Shell-установщик
│
▼
Подсчёт SHA-256
│
▼
Цифровая подпись установщика
При запуске:
Код:
Проверка подписи
│
▼
Извлечение зашифрованного контейнера
│
▼
Запрос ключа или пароля
│
▼
Расшифровка во временный каталог
│
▼
Проверка содержимого
│
▼
Явный запуск установки
---
# Заключение
Полезную нагрузку можно разместить непосредственно внутри Shell-скрипта несколькими способами:
- текст — через Here Document;
- бинарные данные — через Base64;
- набор файлов — через TAR-архив;
- автономный установщик — через Makeself;
- скрытие исходника — через SHC;
- криптографическая защита — через
ageили OpenSSL; - подтверждение подлинности — через цифровую подпись.
Главное — не путать Base64, сжатие и обфускацию с настоящим шифрованием.
Надёжный установщик должен:
1. использовать
set -Eeuo pipefail;2. создавать временный каталог через
mktemp;3. удалять временные данные через
trap;4. проверять контрольную сумму;
5. по возможности проверять цифровую подпись;
6. не хранить пароль внутри исходного кода;
7. не выполнять загруженные данные через
eval;8. не использовать конструкцию
curl | bash;9. позволять извлечь и проверить содержимое до запуска;
10. явно сообщать пользователю, какие изменения будут внесены в систему.
