Дата обновления перевода 2021-07-20

Как сделать чувствительную информацию секретной

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

Когда эти значения чувствительны и должны держаться в тайне, вы можете безопасно хранить их используя систему управления секретами Symfony - иногда называемую “сейфом”.

Note

Система Секретом требует PHP-расширения sodium, которое входит в пакет PHP 7.2. Если вы используете более раннюю версию PHP, вы можете установить PHP-расширение libsodium, или использовать пакет paragonie/sodium_compat.

Генерирование заширофванных ключей

Для того, чтобы зашифровывать и расшифровывать секреты, Symfony необходимы криптографические ключи. Для того, чтобы сгенрировать пару ключей, выполните:

1
$ php bin/console secrets:generate-keys

Это сгенерирует пару асимметричных крипторгафических ключей. Каждое окружение имеет собственный набор ключей. Предполагая, что вы пишете код локально в окружении dev, это создаст:

config/secrets/dev/dev.encrypt.public.php
Используется для шифрования/добавления секретов в сейф. Может быть отправлено безопасно.
config/secrets/dev/dev.decrypt.private.php
Используется для расшифровки/чтения ключей из сейфа. Ключ расшифровки dev может быть отправлен (предполагая, что в сейфе dev не хранится высокочувствительных секретов), но ключ расшифровки prod не нужно отправлять никогда.

Вы можете сгенерировать пару криптографических ключей для окружения prod, выполнив следующее:

1
$ php bin/console secrets:generate-keys --env=prod

Это сгенерирует config/secrets/prod/prod.encrypt.public.php и config/secrets/prod/prod.decrypt.private.php.

Caution

Файл prod.decrypt.private.php высокочувствителен. Ваша команда разработчиков и даже сервисы Непрерывной интеграции не нуждаются в этом ключе. Если ключ расшифровки был выявлен (например, от вас ушел бывший сотрудник), вам нужно подумать о создании нового, путем выполнения secrets:generate-keys --rotate.

Создание или обновление секретов

Предположим, что вы хотите хранить свой пароль базы данных как секрет. Используя команду secrets:set, вы должны добавить этот секрет в сейфы dev и prod:

1
2
3
4
5
6
7
# ввод скрыт во время того, как вы печатаете, из соображений безопасности

# установите ваше значение разработки по умолчанию (может быть переопределено локально)
$ php bin/console secrets:set DATABASE_PASSWORD

# установите ваше значение производства
$ php bin/console secrets:set DATABASE_PASSWORD --env=prod

Это создаст новый файл для секрета в config/secrets/dev и еще один в config/secrets/prod. Вы также можете установить секрет несколькими другими способами:

1
2
3
4
5
6
7
8
# предоставьте файл для считывания секрета
$ php bin/console secrets:set DATABASE_PASSWORD ~/Download/password.json

# или содержание, переданное STDIN
$ echo -n "$DB_PASS" | php bin/console secrets:set DATABASE_PASSWORD -

# или позвольте Symfony сгенерировать рандомное зачение за вас
$ php bin/console secrets:set REMEMBER_ME --random

Ссылание на секреты в файлах конфигурации

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

Если вы сохранили секрет в DATABASE_PASSWORD, вы можете сослаться на него так:

  • YAML
    1
    2
    3
    4
    5
    6
    # config/packages/doctrine.yaml
    doctrine:
        dbal:
            password: '%env(DATABASE_PASSWORD)%'
            # ...
        # ...
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- config/packages/doctrine.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:doctrine="http://symfony.com/schema/dic/doctrine"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/doctrine
            https://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
    
        <doctrine:config>
            <doctrine:dbal
                password="%env(DATABASE_PASSWORD)%"
            />
        </doctrine:config>
    
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // config/packages/doctrine.php
    use Symfony\Config\DoctrineConfig;
    
    return static function (DoctrineConfig $doctrine) {
        $doctrine->dbal()
            ->connection('default')
                ->password('%env(DATABASE_PASSWORD)%')
        ;
    };
    

Реальное значение не будет выполнено во время прогона: компиляция контейнера и разогрев кеша не нуждаются в ключе расшировки.

Список существующих секретов

Все имеют право получить список имент секретов с помощью команды secrets:list. Если у вас есть ключ расшифровки, вы также можете увидеть значения секретов, передав опцию --reveal:

1
2
3
4
5
6
7
$ php bin/console secrets:list --reveal

 ------------------- ------------ --------------------
  Имя                 Значение     Локальное значение
 ------------------- ------------ --------------------
  DATABASE_PASSWORD   "my secret"
 ------------------- ------------ --------------------

Удаление секретов

Symfony предоставляет удобную команду для удаления Секрета:

1
$ php bin/console secrets:remove DATABASE_PASSWORD

Локальные секреты: локальное переопределение секретов

Секреты окружения dev должны содержать хорошие значения по умолчанию для разработки. Но иногда разработчику все равно нужно переопределить секретное значение локально во время разработки.

Большинство команд secrets - включая secrets:set - имеют опцию --local, которая хранит “секрет” в файле .env.{env}.local в качестве стандартной переменной окружения. Чтобы переопределить секрет DATABASE_PASSWORD локально, выполните:

1
$ php bin/console secrets:set DATABASE_PASSWORD --local

Если вы введете root, то теперь вы увидите в вашем файле .env.dev.local следующее:

1
DATABASE_PASSWORD=root

Это переопределит секрет DATABASE_PASSWORD, так как переменные окружения всегда главенствуют над секретами.

Список секретов теперь тоже будет содержать локальную переменную:

1
2
3
4
5
6
$ php bin/console secrets:list --reveal
 ------------------- ------------ --------------------
  Имя                 Значение     Локальное значение
 ------------------- ------------ --------------------
  DATABASE_PASSWORD   "dev value"   "root"
 ------------------- ------------ --------------------

Symfony также предоставляет команду secrets:decrypt-to-local, которая расшифровывает все секреты и сохраняет их в локальном сейфе, и команду secrets:encrypt-from-local для шифрования всех локальных секретов в сейф.

Секреты в окружении тестирования

Если вы добавите секрет в окружения dev и prod, его не будет в окружении test. Вы можете создать “сейф” для окружения test и определить секреты в нем. Но легче будет установить тестовые значения через файл .env.test:

1
2
# .env.test
DATABASE_PASSWORD="testing"

Запуск секретов в производство

В связи с тем, что ключи расшифровки никогда не должны отправляться, вам понадобится вручную сохранять этот файл где-то и запускать его. Есть 2 способа сделать это:

  1. Загрузка файла:

Первая опция - скопировать ключ расшифровки производства - config/secrets/prod/prod.decrypt.private.php на ваш сервер.

  1. Использование переменной окружения

Второй способ - установить переменную окружения SYMFONY_DECRYPTION_SECRET в зашифрованное значение base64 ключа расшифровки производства. Модным способом получения значения ключа является:

1
2
3
# эта команда только получает значение ключа; вы должны также установить переменную окружения
# в вашей системе с этим значением (например, `export SYMFONY_DECRYPTION_SECRET=...`)
$ php -r 'echo base64_encode(require "config/secrets/prod/prod.decrypt.private.php");'

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

1
$ php bin/console secrets:decrypt-to-local --force --env=prod

Это запишет все расшифрованные секреты в файл .env.prod.local. После того, как это будет сделано, ключ расшифровки не должен оставаться на сервере(ах).

Ротация секретов

Команда secrets:generate-keys предоставляет опцию --rotate для регенерирования криптографических ключей. Symfony расшифрует существующие секреты с помощью старого ключа, сгенерирует новые криптографические ключи и повторно зашифрует секреты с новым ключом. Для того, чтобы расшифровать предыдущие секреты, разработчику нужен будет ключ расшифровки.

Конфигурация

Система секретов включена по умолчанию и некоторые аспекты ее поведения можно сконфигурировать:

  • YAML
    1
    2
    3
    4
    5
    6
    # config/packages/framework.yaml
    framework:
        secrets:
            #vault_directory: '%kernel.project_dir%/config/secrets/%kernel.environment%'
            #local_dotenv_file: '%kernel.project_dir%/.env.%kernel.environment%.local'
            #decryption_env_var: 'base64:default::SYMFONY_DECRYPTION_SECRET'
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <!-- config/packages/framework.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:framework="http://symfony.com/schema/dic/framework"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/framework https://symfony.com/schema/dic/framework/framework-1.0.xsd"
    >
        <framework:config secret="%env(APP_SECRET)%">
            <framework:secrets
                vault_directory="%kernel.project_dir%/config/secrets/%kernel.environment%"
                local_dotenv_file="%kernel.project_dir%/.env.%kernel.environment%.local"
                decryption_env_var="base64:default::SYMFONY_DECRYPTION_SECRET"
            />
        </framework:config>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // config/packages/framework.php
    use Symfony\Config\FrameworkConfig;
    
    return static function (FrameworkConfig $framework) {
        $framework->secrets()
            // ->vaultDirectory('%kernel.project_dir%/config/secrets/%kernel.environment%')
            // ->localDotenvFile('%kernel.project_dir%/.env.%kernel.environment%.local')
            // ->decryptionEnvVar('base64:default::SYMFONY_DECRYPTION_SECRET')
        ;
    };
    

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.