GitHub — greatscott/envile: envile: .env Скрытие секретов от посторонних глаз: секреты хранятся в локальных зашифрованных хранилищах (для каждого проекта) и вводятся непосредственно в приложения во время выполнения, никогда не затрагивая диск в виде открытого текста.

GitHub — greatscott/envile: envile: .env Скрытие секретов от посторонних глаз: секреты хранятся в локальных зашифрованных хранилищах (для каждого проекта) и вводятся непосредственно в приложения во время выполнения, никогда не затрагивая диск в виде открытого текста.


Скройте секреты .env от посторонних глаз.

Инструменты кодирования искусственного интеллекта, такие как Cloud Code, Copilot, Cursor и другие, могут читать файлы в каталоге вашего проекта, то есть обычный текст. .env Этот файл представляет собой случайный секретный дамп, ожидающий своего часа. Это не теоретически. Это известная проблема, которая случалась со мной несколько раз (даже после того, как я явно сказал Cloud не заглядывать в файл settings.json CloudCode). enveil Решает эту проблему, обеспечивая секреты в виде открытого текста. никогда не существует на диске. Ваш .env Файл содержит только символические ссылки; Фактические значения находятся в зашифрованном локальном хранилище и вводятся непосредственно в ваш подпроцесс при запуске.

Этот проект вдохновлен решением/сообщением в блоге Филипа Хрика, в котором используется аналогичная концепция с использованием 1Password. Мне нужно было автономное решение, которое не зависело бы от каких-либо сторонних сервисов, породивших это решение. И да, этот проект почти полностью построен на облачном коде с ручной проверкой и тестированием.

Ваш .env Файл выглядит следующим образом:

DATABASE_URL=ev://database_url
STRIPE_KEY=ev://stripe_key
PORT=3000

Технически это безопасно (хотя, возможно, и нет), и что более важно: любой инструмент искусственного интеллекта может случайно (или, возможно, не случайно) заглянуть в него.

когда ты бежишь enveil run -- npm startэто:

  1. Подсказка для вашего главного пароля (никогда не отображается и никогда не отображается в истории оболочки)
  2. Получает 256-битный ключ AES, используя ваш пароль. Аргон2ID (память 64 МБ, 3 итерации)
  3. расшифровывает локальное хранилище AES-256-GCM – Файл хранилища представляет собой 12-байтовый случайный одноразовый номер, за которым следует аутентифицированный зашифрованный текст.
  4. решает каждый ev:// Ссылка на расшифрованную карту
  5. Обнуляет байты ключа и пароля из памяти.
  6. Создает ваш подпроцесс с разрешенными значениями, введенными в его среду.

Файл хранилища представляет собой двоичный объект. Без мастер-пароля его невозможно отличить от случайного шума. Nonce генерируется заново при каждой записи, поэтому повторное использование nonce AES-GCM невозможно. Любая модификация зашифрованного текста – даже перевернутый бит – приводит к сбою аутентификации и отклонению расшифровки.


Через Cargo (после публикации на crates.io)

Это будет рекомендуемый метод установки после публикации Krait. cargo install Создает двоичный файл и помещает его в ~/.cargo/bin/что у тебя уже есть PATH Если вы установили Rust rustup.

Требуется Руст 1.70+.

git clone https://github.com/greatscott/enveil
cd enveil
cargo build --release

Скомпилированный двоичный файл включен target/release/enveil. Установите его один раз на место PATH Таким образом, вы можете запустить его из любого проекта:

macOS/Linux (bash или zsh)

# Option A: ~/.local/bin (no sudo required, common on Linux)
mkdir -p ~/.local/bin
cp target/release/enveil ~/.local/bin/

# Option B: /usr/local/bin (requires sudo, available system-wide)
sudo cp target/release/enveil /usr/local/bin/

# Option C: ~/.cargo/bin (already on PATH if you used rustup)
cp target/release/enveil ~/.cargo/bin/

Если вы использовали вариант А и ~/.local/bin еще не закончил тебя PATHДобавьте это в конфигурацию вашей оболочки (~/.zshrc, ~/.bashrcИли ~/.bash_profile):

export PATH="$HOME/.local/bin:$PATH"

Затем перезагрузите его:

source ~/.zshrc   # or ~/.bashrc

Убедитесь, что это сработало:

Настройка для каждого проекта (запускается один раз для каждого проекта)

Бинарный файл устанавливается глобально — вы никогда не переустанавливаете его. Но у каждого проекта есть свое зашифрованное хранилище:

cd your-project
enveil init

это делает .enveil/ В текущем каталоге с конфигурацией проекта и зашифрованным хранилищем. добавить это .gitignore – Никогда не следует этого совершать.


Запустите это в корне проекта один раз для каждого проекта:

Это генерирует случайную 32-байтовую соль, напишите .enveil/config.tomlСоздает пустое зашифрованное хранилище .enveil/storeИ предлагает установить мастер-пароль. Добавлять .enveil/ для себя .gitignore – Магазин никогда не должен быть совершенным.

enveil set some_database_url
# prompts: Value for 'database_url': (hidden)

enveil set some_api_key

Значения всегда вводятся интерактивно. Невозможно передать значение в качестве аргумента командной строки — это предотвращает появление секретов в истории оболочки. ps выход.

Ссылка на тайну в .env

DATABASE_URL=ev://some_database_url
MY_API_KEY=ev://stripe_key
PORT=3000

Поле KEY=VALUE Линии проходят без изменений. Только ev:// Ссылки решены.

enveil run -- npm start
enveil run -- python manage.py runserver
enveil run -- cargo run

все позже -- ОС поставляется дословно. Подпроцесс наследует всю вашу среду оболочки (так). PATH, HOMEи т. д. существуют) с .env Значения накладываются сверху.

enveil list              # print stored key names (never values)
enveil delete <key>      # remove a secret
enveil import <file>     # encrypt all values in a plaintext .env, rewrite it as ev:// template
enveil rotate            # re-encrypt the store with a new master password

намеренно пропускают заказы

нет никого get больше не надо export. Вывод секретного значения в стандартный вывод создает читаемый AI вектор утечки — весь смысл nlevel в том, чтобы хранить значения вне диска и вне любого читаемого выходного потока.


Каждому инварианту безопасности соответствует соответствующий автоматический тест и путь проверки вручную.

31 тест, охватывающий все приведенные ниже утверждения.


1. Секреты никогда не записываются на диск в виде открытого текста.

Автоматический: store::password::tests::test_encrypt_decrypt_roundtrip

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

cargo test store::password::tests::test_encrypt_decrypt_roundtrip

Ручная проверка:

enveil init          # password: test123
enveil set mykey     # value: my-super-secret

xxd .enveil/store | head -5
strings .enveil/store

xxd Будет отображать двоичные данные. strings Ничего не вернет — нет последовательностей ASCII для извлечения. Первые 12 байтов — это случайный одноразовый номер; Все, что происходит после этого, представляет собой зашифрованный текст AES-GCM с 16-байтовым тегом аутентификации.


2. Обновлять случайный одноразовый номер при каждой записи.

Автоматический: store::password::tests::test_nonce_changes_on_each_save

Сохраняет хранилище дважды подряд, каждый раз считывает первые 12 байт файла и утверждает, что они разные.

cargo test store::password::tests::test_nonce_changes_on_each_save

Ручная проверка:

xxd .enveil/store | head -1    # note the first 12 bytes
enveil set anotherkey          # any write rotates the nonce
xxd .enveil/store | head -1    # first 12 bytes are now different

3. Неправильный пароль выдает ошибку.

Автоматический: store::password::tests::test_wrong_password_returns_err

Создает магазин с паролем, затем пытается разблокировать его другим паролем и запрашивает Err возвращается.

cargo test store::password::tests::test_wrong_password_returns_err

руководство:

enveil list    # enter the wrong password
# output: "Wrong master password or corrupted store."
# exit code: 1

4. Подделанный зашифрованный текст отклоняется (аутентификация AES-GCM).

AES-GCM создает 16-байтовый тег аутентификации в зашифрованном тексте. Любая модификация – даже перевернутый бит – приводит к сбою проверки до того, как можно будет продолжить расшифровку. Простой текст никогда не отображается.

Автоматический: store::password::tests::test_tampered_ciphertext_returns_err

Хранилище переворачивает один байт в области зашифрованного текста файла (до 12-байтового nonce), затем пытается расшифровать и утверждает Err.

cargo test store::password::tests::test_tampered_ciphertext_returns_err

руководство:

# Flip byte 20 (inside ciphertext, past the nonce)
python3 -c "
data = open('.enveil/store', 'rb').read()
bad  = data[:20] + bytes([data[20] ^ 0xFF]) + data[21:]
open('.enveil/store', 'wb').write(bad)
"
enveil list
# output: "Wrong master password or corrupted store."

5. Любая неустраненная серьезная ошибка. ev:// Ссылка

Если какая-либо ссылка .env В магазине нет соответствующего ключа, enveil run Немедленный выход с ненулевым кодом. Подпроцесс никогда не запускается.

Автоматический: env_template::tests::test_unknown_ev_ref_returns_err

Вызов resolve() Со ссылкой, не имеющей совпадающих записей и утверждений. Err.

cargo test env_template::tests::test_unknown_ev_ref_returns_err

руководство:

echo "DB=ev://nonexistent_key" > .env
enveil run -- env
# output: Secret 'nonexistent_key' not found in store. Add it with: enveil set nonexistent_key
# exit code: 1  (the `env` subprocess never ran)

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

2. Интеграция с такими системами, как Keychain и т. д.

Уменьшите необходимость вручную вводить пароль магазина при обновлении.

Leave a Reply

Your email address will not be published. Required fields are marked *