diff --git a/best_practices.rst b/best_practices.rst index a249ea74d4c..5616f1f8120 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -87,6 +87,12 @@ application behavior. :ref:`Use env vars in your project ` to define these options and create multiple ``.env`` files to :ref:`configure env vars per environment `. +Use Secret for Sensitive Information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When your application has sensitive configuration - like an API key - you should +store those securely via :doc:`secrets `. + Use Parameters for Application Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/configuration.rst b/configuration.rst index 8fe6169b8ec..caa2a25cb61 100644 --- a/configuration.rst +++ b/configuration.rst @@ -436,14 +436,14 @@ Configuration Based on Environment Variables Using `environment variables`_ (or "env vars" for short) is a common practice to configure options that depend on where the application is run (e.g. the database -credentials are usually different in production and in your local machine). +credentials are usually different in production versus your local machine). If +the values are sensitive, you can even :doc:`encrypt them as secrets `. -Instead of defining those as regular options, you can define them as environment -variables and reference them in the configuration files using the special syntax +You can reference environment variables using the special syntax ``%env(ENV_VAR_NAME)%``. The values of these options are resolved at runtime (only once per request, to not impact performance). -This example shows how to configure the database connection using an env var: +This example shows how you could configure the database connection using an env var: .. configuration-block:: @@ -485,164 +485,168 @@ This example shows how to configure the database connection using an env var: ] ]); -The next step is to define the value of those env vars in your shell, your web -server, etc. This is explained in the following sections, but to protect your -application from undefined env vars, you can give them a default value using the -``.env`` file: - -.. code-block:: bash - - # .env - DATABASE_URL=sqlite:///%kernel.project_dir%/var/data.db - .. seealso:: The values of env vars can only be strings, but Symfony includes some :doc:`env var processors ` to transform their contents (e.g. to turn a string value into an integer). -In order to define the actual values of env vars, Symfony proposes different -solutions depending if the application is running in production or in your local -development machine. - -Independent from the way you set environment variables, you may need to run the -``debug:container`` command with the ``--env-vars`` option to verify that they -are defined and have the expected values: +To define the value of an env var, you have several options: -.. code-block:: terminal +* :ref:`Add the value to a .env file `; +* :ref:`Encrypt the value as a secret `; +* Set the value as a real environment variable in your shell or your web server. - $ php bin/console debug:container --env-vars - - ---------------- ----------------- --------------------------------------------- - Name Default value Real value - ---------------- ----------------- --------------------------------------------- - APP_SECRET n/a "471a62e2d601a8952deb186e44186cb3" - FOO "[1, "2.5", 3]" n/a - BAR null n/a - ---------------- ----------------- --------------------------------------------- +.. tip:: - # you can also filter the list of env vars by name: - $ php bin/console debug:container --env-vars foo + Some hosts - like SymfonyCloud - offer easy `utilities to manage env vars`_ + in production. - # run this command to show all the details for a specific env var: - $ php bin/console debug:container --env-var=FOO +.. caution:: -.. versionadded:: 4.3 + Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables + or outputting the ``phpinfo()`` contents will display the values of the + environment variables, exposing sensitive information such as the database + credentials. - The option to debug environment variables was introduced in Symfony 4.3. + The values of the env vars are also exposed in the web interface of the + :doc:`Symfony profiler `. In practice this shouldn't be a + problem because the web profiler must **never** be enabled in production. .. _configuration-env-var-in-dev: .. _config-dot-env: -Configuring Environment Variables in Development -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configuring Environment Variables in .env Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of defining env vars in your shell or your web server, Symfony proposes -a convenient way of defining them in your local machine based on a file called -``.env`` (with a leading dot) located at the root of your project. +Instead of defining env vars in your shell or your web server, Symfony provides +a convenient way to define them inside a ``.env`` (with a leading dot) file +located at the root of your project. The ``.env`` file is read and parsed on every request and its env vars are added -to the ``$_ENV`` PHP variable. The existing env vars are never overwritten by -the values defined in ``.env``, so you can combine both. +to the ``$_ENV`` & ``$_SERVER`` PHP variables. Any existing env vars are *never* +overwritten by the values defined in ``.env``, so you can combine both. -This is for example the content of the ``.env`` file to define the value of the -``DATABASE_URL`` env var shown earlier in this article: +For example, to define the ``DATABASE_URL`` env var shown earlier in this article, +you can add: .. code-block:: bash # .env DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" +This file should be committed to your repository and (due to that fact) should +only contain "default" values that are good for local development. This file +should not contain production values. + In addition to your own env vars, this ``.env`` file also contains the env vars defined by the third-party packages installed in your application (they are added automatically by :ref:`Symfony Flex ` when installing packages). +.. _configuration-multiple-env-files: + +Overriding Environment Values via .env.local +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to override an environment value (e.g. to a different value on your +local machine), you can do that in a ``.env.local`` file: + +.. code-block:: bash + + # .env.local + DATABASE_URL="mysql://root:@127.0.0.1:3306/my_database_name" + +This file should be ignored by git and should *not* be committed to your repository. +Several other ``.env`` files are available to set environment variables in *just* +the right situation: + +* ``.env``: defines the default values of the env vars needed by the application; +* ``.env.local``: overrides the default values for all environments but only on + the machine which contains the file. This file should not be committed to the + repository and it's ignored in the ``test`` environment (because tests should + produce the same results for everyone); +* ``.env.`` (e.g. ``.env.test``): overrides env vars only for one + environment but for all machines (these files *are* committed); +* ``.env..local`` (e.g. ``.env.test.local``): defines machine-specific + env var overrides only for one environment. It's similar to ``.env.local``, + but the overrides only apply to one environment. + +*Real* environment variables always win over env vars created by any of the +``.env`` files. + +The ``.env`` and ``.env.`` files should be committed to the +repository because they are the same for all developers and machines. However, +the env files ending in ``.local`` (``.env.local`` and ``.env..local``) +**should not be committed** because only you will use them. In fact, the +``.gitignore`` file that comes with Symfony prevents them from being committed. + +.. caution:: + + Applications created before November 2018 had a slightly different system, + involving a ``.env.dist`` file. For information about upgrading, see: + :doc:`configuration/dot-env-changes`. + .. _configuration-env-var-in-prod: Configuring Environment Variables in Production ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In production, the ``.env`` files are also parsed and loaded on each request so -you can add env vars to those already defined in the server. In order to improve -performance, you can run the ``dump-env`` command (available when using -:ref:`Symfony Flex ` 1.2 or later). +In production, the ``.env`` files are also parsed and loaded on each request. So +the easiest way to define env vars is by deploying a ``.env.local`` file to your +production server(s) with your production values. -This command parses all the ``.env`` files once and compiles their contents into -a new PHP-optimized file called ``.env.local.php``. From that moment, Symfony -will load the parsed file instead of parsing the ``.env`` files again: +To improve performance, you can optionally run the ``dump-env`` command (available +in :ref:`Symfony Flex ` 1.2 or later): .. code-block:: terminal + # parses ALL .env files and dumps their final values to .env.local.php $ composer dump-env prod +After running this command, Symfony will load the ``.env.local.php`` file to +get the environment variables and will not spend time parsing the ``.env`` files. + .. tip:: Update your deployment tools/workflow to run the ``dump-env`` command after each deploy to improve the application performance. -.. _configuration-env-var-web-server: +.. _configuration-secrets: -Creating ``.env`` files is the easiest way of using env vars in Symfony -applications. However, you can also configure real env vars in your servers and -operating systems. +Encrypting Environment Variables (Secrets) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. tip:: - - SymfonyCloud, the cloud service optimized for Symfony applications, defines - some `utilities to manage env vars`_ in production. - -.. caution:: +Instead of defining a real environment variable or adding it to a ``.env`` file, +if the value of a variable is sensitive (e.g. an API key or a database password), +you can encrypt the value using the :doc:`secrets management system `. - Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables - or outputting the ``phpinfo()`` contents will display the values of the - environment variables, exposing sensitive information such as the database - credentials. - - The values of the env vars are also exposed in the web interface of the - :doc:`Symfony profiler `. In practice this shouldn't be a - problem because the web profiler must **never** be enabled in production. - -.. _configuration-multiple-env-files: - -Managing Multiple .env Files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Listing Environment Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``.env`` file defines the default values for all env vars. However, it's -common to override some of those values depending on the environment (e.g. to -use a different database for tests) or depending on the machine (e.g. to use a -different OAuth token on your local machine while developing). +Regardless of how you set environment variables, you can see a full list with +their values by running: -That's why you can define multiple ``.env`` files to override env vars. The -following list shows the files loaded in all environments. The ``.env`` file is -the only mandatory file and each file content overrides the previous one: +.. code-block:: terminal -* ``.env``: defines the default values of the env vars needed by the application; -* ``.env.local``: overrides the default values of env vars for all environments - but only in the machine which contains the file (e.g. your development computer). - This file should not be committed to the repository and it's ignored in the - ``test`` environment (because tests should produce the same results for everyone); -* ``.env.`` (e.g. ``.env.test``): overrides env vars only for some - environment but for all machines; -* ``.env..local`` (e.g. ``.env.test.local``): defines machine-specific - env vars overrides only for some environment. It's similar to ``.env.local``, - but the overrides only apply to some particular environment. + $ php bin/console debug:container --env-vars -.. note:: + ---------------- ----------------- --------------------------------------------- + Name Default value Real value + ---------------- ----------------- --------------------------------------------- + APP_SECRET n/a "471a62e2d601a8952deb186e44186cb3" + FOO "[1, "2.5", 3]" n/a + BAR null n/a + ---------------- ----------------- --------------------------------------------- - The real environment variables defined in the server always win over the - env vars created by the ``.env`` files. + # you can also filter the list of env vars by name: + $ php bin/console debug:container --env-vars foo -The ``.env`` and ``.env.`` files should be committed to the shared -repository because they are the same for all developers and machines. However, -the env files ending in ``.local`` (``.env.local`` and ``.env..local``) -**should not be committed** because only you will use them. In fact, the -``.gitignore`` file that comes with Symfony prevents them from being committed. + # run this command to show all the details for a specific env var: + $ php bin/console debug:container --env-var=FOO -.. caution:: +.. versionadded:: 4.3 - Applications created before November 2018 had a slightly different system, - involving a ``.env.dist`` file. For information about upgrading, see: - :doc:`configuration/dot-env-changes`. + The option to debug environment variables was introduced in Symfony 4.3. .. _configuration-accessing-parameters: diff --git a/configuration/secrets.rst b/configuration/secrets.rst new file mode 100644 index 00000000000..f1294143732 --- /dev/null +++ b/configuration/secrets.rst @@ -0,0 +1,320 @@ +.. index:: + single: Secrets + +How to Keep Sensitive Information Secret +======================================== + +.. versionadded:: 4.4 + + The Secrets management was introduced in Symfony 4.4. + +:ref:`Environment variables ` are the best way to store configuration +that depends on where the application is run - for example, some API key that +might be set to one value while developing locally and another value on production. + +When these values are *sensitive* and need to be kept private, you can safely +store them by using Symfony's secrets management system - sometimes called a +"vault". + +.. note:: + + The Secrets system requires the sodium PHP extension that is bundled + with PHP 7.2. If you're using an earlier PHP version, you can + install the `libsodium`_ PHP extension or use the + `paragonie/sodium_compat`_ package. + +.. _secrets-generate-keys: + +Generate Cryptographic Keys +--------------------------- + +In order to encrypt and decrypt **secrets**, Symfony needs **cryptographic keys**. +A pair of keys can be generated by running: + +.. code-block:: terminal + + $ php bin/console secrets:generate-keys + +This will generate a pair of asymmetric **cryptographic keys**. Each +:ref:`environment ` has its own set of keys. Assuming +you're coding locally in the ``dev`` environment, this will create: + +``config/secrets/dev/dev.encrypt.public.php`` + Used to encrypt/add secrets to the vault. Can be safely committed. + +``config/secrets/dev/dev.decrypt.private.php`` + Used to decrypt/read secrets from the vault. The ``dev`` decryption key can + be committed (assuming no highly-sensitive secrets are stored in the dev vault) + but the ``prod`` decryption key should *never* be committed. + +You can generate a pair of cryptographic keys for the ``prod`` environment by +running: + +.. code-block:: terminal + + $ php bin/console secrets:generate-keys --env=prod + +This will generate ``config/secrets/prod/prod.encrypt.public.php`` and +``config/secrets/prod/prod.decrypt.private.php``. + +.. caution:: + + The ``prod.decrypt.private.php`` file is highly sensitive. Your team of developers + and even Continuous Integration services don't need that key. If the + **decryption key** has been exposed (ex-employee leaving for instance), you + should consider generating a new one by running: + ``secrets:generate-keys --rotate``. + +.. _secrets-set: + +Create or Update Secrets +------------------------ + +Suppose you want to store your database password a secret. By using the +``secrets:set`` command, you should add this secret to both the ``dev`` *and* +``prod`` vaults: + +.. code-block:: terminal + + # the input is hidden as you type for security + + # set your a default development value (can be overridden locally) + $ php bin/console secrets:set DATABASE_PASSWORD + + # set your production value + $ php bin/console secrets:set DATABASE_PASSWORD --env=prod + +This will create a new file for the secret in ``config/secrets/dev`` and another +in ``config/secrets/prod``. You can also set the secret in a few other ways: + +.. code-block:: terminal + + # provide a file where to read the secret from + $ php bin/console secrets:set DATABASE_PASSWORD ~/Download/password.json + + # or contents passed to STDIN + $ echo -n "$DB_PASS" | php bin/console secrets:set DATABASE_PASSWORD - + + # or let Symfony generate a random value for you + $ php bin/console secrets:set REMEMBER_ME --random + +Referencing Secrets in Configuration Files +------------------------------------------ + +Secret values can be referenced in the same way as +:ref:`environment variables`. Be careful that you don't +accidentally define a secret *and* an environment variable with the same name: +**environment variables override secrets**. + +If you stored a ``DATABASE_PASSWORD`` secret, you can reference by: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/doctrine.yaml + doctrine: + dbal: + password: '%env(DATABASE_PASSWORD)%' + # ... + # ... + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // config/packages/doctrine.php + $container->loadFromExtension('doctrine', [ + 'dbal' => [ + 'password' => '%env(secret:DATABASE_PASSWORD)%', + ] + ]); + +The actual value will be resolved at runtime: container compilation and cache +warmup don't need the **decryption key**. + +List Existing Secrets +--------------------- + +Everybody is allowed to list the secrets names with the command +``secrets:list``. If you have the **decryption key** you can also reveal the +secrets' values by passing the ``--reveal`` option: + +.. code-block:: terminal + + $ php bin/console secrets:list --reveal + + ------------------- ------------ ------------- + Name Value Local Value + ------------------- ------------ ------------- + DATABASE_PASSWORD "my secret" + ------------------- ------------ ------------- + +Remove Secrets +-------------- + +Symfony provides a convenient command to remove a Secret: + +.. code-block:: terminal + + $ php bin/console secrets:remove DATABASE_PASSWORD + +Local secrets: Overriding Secrets Locally +----------------------------------------- + +The ``dev`` environment secrets should contain nice default values for development. +But sometimes a developer *still* needs to override a secret value locally when +developing. + +Most of the ``secrets`` commands - including ``secrets:set`` - have a ``--local`` +option that stores the "secret" in the ``.env.{env}.local`` file as a standard +environment variable. To override the ``DATABASE_PASSWORD`` secret locally, run: + +.. code-block:: terminal + + $ php bin/console secrets:set DATABASE_PASSWORD --local + +If you entered ``root``, you will now see this in your ``.env.dev.local`` file: + +.. code-block:: bash + + DATABASE_PASSWORD=root + +This will *override* the ``DATABASE_PASSWORD`` secret because environment variables +always take precedence over secrets. + +Listing the secrets will now also display the local variable: + +.. code-block:: terminal + + $ php bin/console secrets:list --reveal + ------------------- ------------- ------------- + Name Value Local Value + ------------------- ------------- ------------- + DATABASE_PASSWORD "dev value" "root" + ------------------- ------------- ------------- + +Symfony also provides the ``secrets:decrypt-to-local`` command to decrypts +all secrets and stores them in the local vault and ``secrets:encrypt-from-local`` +to encrypt all local secrets to the vault. + +Secrets in the test Environment +------------------------------- + +If you add a secret in the ``dev`` and ``prod`` environments, it will be missing +from the ``test`` environment. You *could* create a "vault" for the ``test`` +environment and define the secrets there. But an easier way is to set the test +values via the ``.env.test`` file: + +.. code-block:: bash + + # .env.test + DATABASE_PASSWORD="testing" + +Deploy Secrets to Production +---------------------------- + +Due to the fact that decryption keys should never be committed, you will need to +manually store this file somewhere and deploy it. There are 2 ways to do that: + +1) Uploading the file: + +The first option is to copy the **decryption key** - +``/config/secrets/prod/prod.decrypt.private.php`` to your server(s). + +2) Using an Environment Variable + +The second way is to set the ``SYMFONY_DECRYPTION_SECRET`` environment variable +to the base64 encoded value of the **decryption key**. A fancy way to fetch the +value of the key is: + +.. code-block:: terminal + + $ php -r 'echo base64_encode(require "config/secrets/prod/prod.decrypt.private.php");' + +To improve performance (i.e. avoid decrypting secrets at runtime), you can decrypt +your secrets during deployment to the "local" vault: + +.. code-block:: terminal + + $ php bin/console secrets:decrypt-to-local --force --env=prod + +This will put all the decrypted secrets into ``.env.php.local``. After doing this, +the decryption key does *not* need to remain on the server. + +Rotating Secrets +---------------- + +The ``secrets:generate-keys`` command provides a ``--rotate`` option to +regenerate the **cryptographic keys**. Symfony will decrypt existing secrets with +the old key, generate new **cryptographic keys** and re-encrypt secrets with the +new key. In order to decrypt previous secrets, the developer must have the +**decryption key**. + +Configuration +------------- + +The secrets system is enabled by default and some of its behavior can be configured: + +.. configuration-block:: + + .. code-block:: yaml + + # 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' + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + $container->loadFromExtension('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', + ], + ]); + + +.. _`libsodium`: https://pecl.php.net/package/libsodium +.. _`paragonie/sodium_compat`: https://github.com/paragonie/sodium_compat diff --git a/doctrine/pdo_session_storage.rst b/doctrine/pdo_session_storage.rst index 7bc6bacc450..8123a68460d 100644 --- a/doctrine/pdo_session_storage.rst +++ b/doctrine/pdo_session_storage.rst @@ -69,8 +69,9 @@ To use it, first register a new handler service: .. tip:: Configure the database credentials - :ref:`using environment variables in the config file ` - to make your application more secure. + :ref:`using environment variables ` + or as a :doc:`secret ` to make your application + more secure. Next, tell Symfony to use your service as the session handler: