Every mid-sized to big web application has to deal with sensitive information. Examples of this type of information are credentials to access databases and third-party APIs keys.
If this data is not encrypted at rest, attackers might get access to it and use it for malicious purposes.
When you first start, it's very common to find this information stored right into the code where the value is being used (a.k.a. hardcoded) and while this works, it quickly becomes obvious that it's not a good practice for the long run since making a small change means going through hundreds or even thousands of lines of code.
The next evolutionary step is to take these values and dynamically generate a config file during deployment (e.g. GitHub Action)
Or some other more elaborate formats such as YAML, XML or INI.
The advantage of using external files is the fact that when change is needed there's a well known (and single!) location to make those changes and things just keep working as expected.
But the problem is where to store these secrets? If they're embedded in a GitHub Action file, they still don't offer much secrecy as any person with access to the codebase can view the values you're supposed to be keeping away from unwanted eyes.
One very good way to solve this problem is configuring applications using environment variable as then, no app config values live in the codebase.
One important note: this mechanism is by no means exclusive to PHP and is just as useful if you're working with Nodejs, Python or pretty much any other programming language.
If you were to use an environment variable in a PHP CLI application you could call it using a command such as:
And this value would be available to your script via the getenv function function, like this:
There's another way to do it, using the superglobal $_ENV, like this:
I recommend only using getenv as in order to use the $_ENV superglobal, your php.ini file requires the value for variables_order starting with a letter "E" which may not always be the case.
Setting environment variables for a CLI application is simple, but what about in a web environment where the PHP process is spawned by an application server such as NGINX or Apache?
Application servers (for good security reasons) don't expose system-level environment variables to the PHP process, so you'll need a different approach.
Let's see how you can set environment variables for the most popular web servers: Apache and NGINX and we'll be using Docker so you can easily follow along with the following examples.
How to Set Environment Variables for PHP Applications in Apache
Apache uses the mod_env module for defining environment variables using the SetEnv directive.
The best way to do this for PHP in Apache is by creating a separate Apache configuration file to will their values.
Using Debian and Ubuntu as an example, the config file containing the environment variables should be stored in the /etc/apache2/conf-available directory.
In our case we'll call it apache-env-vars.conf, using the SetEnv directive to define each environment variable:
After doing this, the configuration must be enabled in order for Apache to load it when starting, but how this is done depends upon where you're running Apache.
If using a Virtual Machine running Debian or Ubuntu, you'll need to create a conf file at /etc/apache2/conf/env-vars.conf, then run a2enconf env-vars to enable it to be loaded on startup.
If Apache is already running when this file is created, then must restart Apache by running sudo service apache2 restart in order for the environment variables in the conf file to be loaded.
Using Docker and the php:7-apache image is similar except the conf file will need to be created in the $APACHE_CONFDIR/conf-available/ directory, then run a2enconf env-vars as part of the container start commands.
When it comes to NGINX, a similar strategy should be applied, the main difference is that NGINX doesn't spawn PHP processes directly, as it only proxies requests to PHP-FPM. This means the environment variables must be defined in a PHP-FPM configuration file, typically stored in /usr/local/etc/php-fpm.d/.
Setting environment variables for PHP-FPM uses the following syntax (do not quote values):
So, in order to store your environment variables in NGINX and PHP-FPM, it's a good idea to create a new file at /usr/local/etc/php-fpm.d/env-vars.conf and put the definitions in it.
Just as in the case of Apache, it makes sense to create this file when running the container.
Environment variables are the best solution for providing your PHP application with secrets and configuration data, but where should they be stored? We know they should not be stored in source code, but what are the other options?
One problem with storing environment variables values in the web server or the operating system configuration files is the fact that they live outside of your project, which increases the complexity of deploying your code in multiple locations.
An .env file is a plain text file which contains environment variables definitions which are designed so your PHP application will parse them, bypassing the Apache, NGINX and PHP-FPM.
The usage of .env files is popular in many PHP frameworks such as Laravel which has built-in support for parsing .env files, or using the vlucas/phpdotenv library.
Using .env files in PHP is simple, all that's needed is a mechanism to read the file, parse its contents and populate the environment, right?
Well... actually there are a few other issues to pay attention to such as validation and checking for existence of required values.
As usual, why go through all this trouble when someone else has already done it for you, right? Exactly, you can leverage the vlucas/dotenv package and simply make use of it by requiring it using composer.
To add this package to your application, run composer require vlucas/dotenv then require the autoload file located at your vendor directory like this:
And you'll be ready to use the library like this:
After that, you can simply access the values stored in the .env file using getenv.
Clearly, since .env files hold potentially sensitive data they should not be stored in the publicly accessible webroot as NGINX and Apache see them as a text file and don't know they should be protected by default. And they definitely should never be committed to your source code repository.
Usually, they are stored in the root directory of your application.
Laravel uses .env files as part of its configuration and automatically bundles in the vlucas/dotenv package. In fact, when you create a new project a sample .env file is created for you.
One difference you can find when working with .env files in Laravel projects as opposed to non-Laravel ones is the existence of a helper called env which can be used to access the values of environment variables, like this:
While there's the possibility of using the second parameter to provide a default value, it's not recommended. Why? Because having to browse PHP code in order to see how a default value was set is confusing. It's therefore much better to to define everything in the .env file so there is only one place to look to see how an application is configured.
Values stored within .env files can be passed to PHP applications in Docker in several ways.
One option is to populate the environment variables for the container using the --env-file option.
The limitation of this approach is that it's only useful for PHP CLI applications, as container (system) defined environment variables will not be passed through by web servers for security reasons.
A more common way to use an .env file would be to mount it into the docker container:
From there, your application can access the .env file stored at /usr/src/app/.env within the container simply by doing the following:
But before deciding that .env files are the way to go, it's important to understand the risks and drawbacks to using .env files for app config and secrets.
While .env files are certainly better than hard-coding secrets in source code, they create a new set of problems:
So... how should you handle your application's secrets?
Using a Secrets Manager for PHP Applications
In order to avoid these problems and others, using an environment variables manager such as Doppler can make life easier.
All of the environment variables for your application can be easily managed through the Doppler dashboard and secrets can be fetched using their CLI or API.
To see what it's like to use a secrets manager with PHP, let's look at a couple of examples using Doppler to populate the secrets to PHP applications using environment variables.
The general idea of the following examples is to use Doppler as the single source of truth when it comes to secrets management and use the Doppler CLI to download secrets and make them available to your Docker PHP application.
There are basically two decisions to be made here:
In the following sections, I'll explore the different options so you can choose the one that makes most sense for you.
If you prefer to keep your Docker image as is, you can run the Doppler CLI in the deployment or container hosting environment to generate the conf file and mount inside the container.
The exact commands you'll need to start your container depend on whether you choose configuration or .env files.
We'll use Apache conf files as the example here, but the process is essentially the same, just syntactically different for PHP-FPM or a .env file.
External Apache Conf File Mount
To download the latest version of your secrets into an Apache configuration file, you'll need to use the Doppler CLI to generate the Apache conf file before running your container:
The mount the env-vars.conf file inside the container:
Apache will then automatically read the conf when starting up.
If you prefer using .env files, the command to download the secrets is slightly different:
Then you can start your container like this:
These examples allow you to use Doppler without modifying an existing image such as php:7.4-apache. Now let's take a look at the recommended approach which uses a custom Docker image with the Doppler CLI embedded.
Another way to go about it is to have the Doppler CLI inside the container and have it generate the Apache conf file at container run time. The benefit of this approach is that all you have to do to get the latest version of your secrets is simply restart the container. No redeploy is necessary, and no files to mount.
Using Apache as an example, we'll need to extend the php:7-apache Docker image to embed the Doppler CLI and change the entrypoint script so it generates the Apache conf file before starting Apache.
Here is a complete Dockerfile example:
The code for the custom docker-php-entrypoint would be something like the following:
Be sure to give the entrypoint executable permissions:
Now let's build the Docker image:
Go to the Doppler dashboard for your project, generate a Service Token, then export that in your terminal:
And finally, run the container:
This article is already really long, but I'd encourage you to check out this sample repository from Doppler that has additional working examples of concepts that I've demonstrated in this article.
If you manage your deployments with Laravel Forge, integrating Doppler is as easy as a few clicks to have your secrets automatically available to your applications and updated all the time.
Just follow the instructions here.
Another nice feature of Doppler is it's API which allows you to create custom clients, perhaps interact with it from within your application.
All you have to do is issue a REST request to Doppler's server, provide your access token and you're good to go.
Here's an example using cURL:
Of course, you can use the HTTP client of your choice but bear in mind that hitting the API on every request will be extremely bad for the performance of your applications so you'll likely want to save the output from Doppler's API to a local config file so it only needs to be done for the first request after deployment.
If you want to know more about Doppler's API check out this doc.
In this article, you've learned different ways to provide secrets to your PHP application covering Apache, NGINX with PHP-FPM and .env files. If you want to give Doppler a try you can sign up for a free community plan.
PHP environment variables can be stored in different ways, such as in .env files for your application to parse or within web server configurations like Apache or NGINX. They are used to provide your PHP application with configuration data and secrets without storing them in the source code.
Adding C:\PHP to the path environment variable involves accessing the System Properties in Windows, navigating to the Advanced tab, clicking on Environment Variables, and then appending ;C:\\PHP to the end of the Path variable under System Variables.
You can check PHP environment variables by using the getenv() function or accessing the $_ENV superglobal in your PHP scripts. This allows you to retrieve the values of environment variables set in your system or web server configuration.
Trusted by the world’s best DevOps and security teams. Doppler is the secrets manager developers love.