<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chris Cornutt</title>
    <description>The latest articles on DEV Community by Chris Cornutt (@enygma).</description>
    <link>https://dev.to/enygma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4532%2F96653359-ca04-4434-8b24-90b7c69fa842.jpg</url>
      <title>DEV Community: Chris Cornutt</title>
      <link>https://dev.to/enygma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/enygma"/>
    <language>en</language>
    <item>
      <title>Securing Credentials for PHP with Docker</title>
      <dc:creator>Chris Cornutt</dc:creator>
      <pubDate>Sun, 07 Jul 2019 14:23:41 +0000</pubDate>
      <link>https://dev.to/enygma/securing-credentials-for-php-with-docker-210p</link>
      <guid>https://dev.to/enygma/securing-credentials-for-php-with-docker-210p</guid>
      <description>&lt;p&gt;** &lt;a href="https://websec.io/2018/07/22/Docker-Secure-Credentials.html"&gt;Previously posted&lt;/a&gt; on my site, Websec.io&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://dev.to/enygma/keeping-credentials-secure-in-php-1mgo"&gt;previous post&lt;/a&gt; I covered one method you can use to secure the credentials in your PHP application. In that article, I provided an example specific to the use of Apache and its &lt;code&gt;envvars&lt;/code&gt; handling to read in values and pass them along to the waiting PHP process as &lt;code&gt;$_ENV&lt;/code&gt; variables. This in combination with &lt;a href="https://github.com/psecio/secure_dotenv"&gt;the psecio/secure_dotenv library&lt;/a&gt; allowed you to pass along an encryption key that could be used to decrypt values from the application's &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;While this works for a flat Apache and PHP environment, the world has moved beyond that basic setup and has moved to using another popular environment building tool: &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;. Docker makes it possible to create environments out of interconnected containers that are specialized for specific purposes. This makes it easier to replace technologies in your stack with new versions and makes the containers more reusable. A simple configuration file is all it takes to set up an environment and can be used to rebuild it at any time.&lt;/p&gt;

&lt;p&gt;So, if we move forward with current technology, we need a way to secure our credentials in a Docker-based environment that makes use of PHP-FPM and Nginx. Fortunately, there's a relatively simple way to handle this with just a few configuration changes. To make the setup even more robust, I'm also going to show you how to integrate &lt;a href="https://www.vaultproject.io/"&gt;Vault&lt;/a&gt; into the flow for secrets storage. &lt;/p&gt;

&lt;p&gt;Vault is a project from Hashicorp that is specifically designed to protect secret values. To be able to access the values, you "unlock" the service and can then fetch your secret values via an API. Once complete, you can "lock" the service back up, preventing anyone without lock/unlock access from reaching the secret values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Overview
&lt;/h2&gt;

&lt;p&gt;Before we dive into the configurations and files required to make this all happen, I wanted to take a brief look at the pieces involved here and how they'll fit together. I've mentioned some of them above in passing but here's the list all in one place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; to build and manage the environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; to handle the web requests and responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP-FPM&lt;/strong&gt; to parse and execute the PHP for the request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vault&lt;/strong&gt; to store and manage the secrets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll also be making use of &lt;a href="https://github.com/psecio/vaultlib"&gt;a simple Vault client - psecio/vaultlib&lt;/a&gt; to make the requests to Vault for the secrets. Using a combination of these technologies and a bit of configuration a working system isn't too difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting Credentials
&lt;/h2&gt;

&lt;p&gt;There are several ways to get secrets into a Docker-based environment, some being more secure than others. Here's a list of some of these options and their pros and cons:&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing them in as command-line options
&lt;/h3&gt;

&lt;p&gt;One option that Docker allows is the passing in of values on the command-line when you're bringing up the container. For example, if you wanted to execute a command inside of a container and pass in values that become environment variables, you could use the &lt;code&gt;-e&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "test=foo" whoami
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this command, we're executing the &lt;code&gt;whoami&lt;/code&gt; command and passing in an environment variable of &lt;code&gt;test&lt;/code&gt; with a value of &lt;code&gt;foo&lt;/code&gt;. While this is useful, it's limited to only being used in single commands and not in the environment as a whole when it starts up. Additionally, when you run a command on the command-line, the command and all of its arguments could show up in the process list. This would expose the plain-text version of the variable to anyone with access to the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Docker "secrets"
&lt;/h3&gt;

&lt;p&gt;Another option that's one of the most secure in the list is the use of Docker's own "secrets" handling. &lt;a href="https://docs.docker.com/engine/swarm/secrets/"&gt;This functionality&lt;/a&gt; allows you to store secret values inside of an encrypted storage location but still allows them to be accessed from inside of the Docker containers. You use the &lt;code&gt;docker secret&lt;/code&gt; command to set the value and grant access to the services that should have access. Their &lt;a href="https://docs.docker.com/engine/swarm/secrets/"&gt;documentation&lt;/a&gt; has several examples of setting it up and how to use it in more real-world situations (such as a WordPress blog).&lt;/p&gt;

&lt;p&gt;While this storage option is one of the better ones, it also comes with a caveat: it can only be used in a Docker Swarm situation. Docker Swarm is functionality built into Docker that makes it easier to manage a cluster of Docker instances rather than just one. If you're not using Swarm mode, you're out of luck on using this "secrets" storage method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hard-coding them in the &lt;code&gt;docker-compose&lt;/code&gt; configuration
&lt;/h3&gt;

&lt;p&gt;There's another option with Docker Compose to get values pushed into the environment as variables: through settings in the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Before I get too far along in the setup, I want to outline the file and directory structure of what we'll be working with. There are several configuration files involved and I wanted to call them out so they're all in place.&lt;/p&gt;

&lt;p&gt;For the examples, we'll be working in a &lt;code&gt;project1/&lt;/code&gt; directory which will contain the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;www.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;site.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Staring with Docker
&lt;/h2&gt;

&lt;p&gt;To start, we need to build out the environment our application is going to live in. This is a job for Docker or, more specifically &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;. For those not familiar with Docker Compose, you can think of it as a layer that sits on top of Docker and makes building out the environments simpler than having a bunch of &lt;code&gt;Dockerfile&lt;/code&gt; configuration files lying around. It joins the different containers together as "services" and provides a configuration structure that abstracts away much of the manual commands that just using the &lt;code&gt;docker&lt;/code&gt; command line tool would require.&lt;/p&gt;

&lt;p&gt;In a Compose configuration file, you define the "services" that you want to create and various settings about them. For example, if we just wanted to create a simple server with Nginx running on port &lt;code&gt;8080&lt;/code&gt;, we could create a &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Easy, right? You can create the same kind of thing with just &lt;code&gt;Dockerfile&lt;/code&gt; configurations but Compose makes it a bit simpler. &lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration
&lt;/h3&gt;

&lt;p&gt;Using this structure we're going to create our environment that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A container running Nginx that mounts our &lt;code&gt;code/&lt;/code&gt; directory to its document root&lt;/li&gt;
&lt;li&gt;A container running PHP-FPM (PHP 7) to handle the incoming PHP requests (linked to the Nginx container)&lt;/li&gt;
&lt;li&gt;The Vault container that runs the Vault service (linked to the PHP container)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./code:/code
      - ./site.conf:/etc/nginx/conf.d/site.conf
    links:
      - php

  php:
    image: php:7-fpm
    volumes:
      - ./www.conf:/usr/local/etc/php-fpm.d/www.conf
      - ./code:/code
    environment:
      - VAULT_KEY=${VAULT_KEY}
      - VAULT_TOKEN=${VAULT_TOKEN}
      - ENC_KEY=${ENC_KEY}

  vault:
    image: vault:latest
    links:
      - php
    environment:
      - VAULT_ADDR=http://127.0.0.1:8200
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lets walk through this so you can understand each part. First we create the &lt;code&gt;web&lt;/code&gt; service - this is our Nginx container that installs from the &lt;code&gt;nginx:lastest&lt;/code&gt; image. It then defines the ports to use, setting up the container to respond on port &lt;code&gt;8080&lt;/code&gt; and proxy that to port &lt;code&gt;80&lt;/code&gt; on the local machine (the default port for &lt;code&gt;HTTP&lt;/code&gt;). The &lt;code&gt;volumes&lt;/code&gt; section defines two things to mount from the local system to the remote system: our &lt;code&gt;code/&lt;/code&gt; directory and the &lt;code&gt;site.conf&lt;/code&gt; that's copied over to the Nginx configuration path of &lt;code&gt;/etc/nginx/conf.d/site.conf&lt;/code&gt;. Finally, in the &lt;code&gt;links&lt;/code&gt; section, we tell Docker that we want to link the &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;php&lt;/code&gt; containers so they're aware of each other. This link makes it possible for the Nginx configuration to be able to call PHP-FPM as a handler on &lt;code&gt;*.php&lt;/code&gt; requests. The contents of the &lt;code&gt;site.conf&lt;/code&gt; file are explained in a section later in this article.&lt;/p&gt;

&lt;p&gt;Next is the &lt;code&gt;php&lt;/code&gt; service. This service installs from the &lt;code&gt;php:7-fpm&lt;/code&gt; image, loading in the latest version of PHP-FPM that uses a &lt;code&gt;7.x&lt;/code&gt; version. Again we have a &lt;code&gt;volumes&lt;/code&gt; section that copies over the &lt;code&gt;code/&lt;/code&gt; to the container but this time we're moving in a different configuration file: the &lt;code&gt;www.conf&lt;/code&gt; configuration. This is the configuration PHP-FPM uses when processing PHP requests. More on this configuration will be shared later too. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What about the &lt;code&gt;environment&lt;/code&gt; settings in the &lt;code&gt;php&lt;/code&gt; service, you might be asking. Don't worry, I'll get to those later but those are one of the keys to how we'll be getting values from Docker pushed into the service containers for later use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, we get to the &lt;code&gt;vault&lt;/code&gt; service. This service uses the &lt;code&gt;vault:latest&lt;/code&gt; image to pull in the latest version of the Vault container and runs the setup process. There's also a link over to the &lt;code&gt;php&lt;/code&gt; service so that Vault and PHP can talk. The last part there, the &lt;code&gt;environment&lt;/code&gt; setting, is just a Vault-specific setting so that we know a predictable address and port to access the Vault service from PHP.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;site.conf&lt;/code&gt; configuration (Nginx)
&lt;/h3&gt;

&lt;p&gt;I mentioned this configuration before when walking through the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration but lets get into a bit more detail. First, here's the contents of our &lt;code&gt;site.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    index index.php index.html;
    server_name php-docker.local;
    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /code;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you've ever worked with PHP-FPM and Nginx before, this configuration probably looks pretty similar. It sets up the server configuration (with the hostname of &lt;code&gt;php-docker.local&lt;/code&gt;, add this to &lt;code&gt;127.0.0.1&lt;/code&gt; in &lt;code&gt;/etc/hosts&lt;/code&gt;) to hand off any requests for &lt;code&gt;.php&lt;/code&gt; scripts to PHP-FPM via FastCGI. Our &lt;code&gt;index&lt;/code&gt; setting lets us use either a &lt;code&gt;index.php&lt;/code&gt; or &lt;code&gt;index.html&lt;/code&gt; file for the base without having to specify it in the URL. Pretty simple, right?&lt;/p&gt;

&lt;p&gt;When we fire up Docker Compose this configuration will be copied into the container at the &lt;code&gt;/etc/nginx/conf.d/site.conf&lt;/code&gt; path. With that settled, we'll move on to the next file: the PHP-FPM configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;www.conf&lt;/code&gt; configuration (PHP-FPM)
&lt;/h3&gt;

&lt;p&gt;This configuration sets up how the PHP-FPM process behaves when Nginx passes the incoming request over to it. I've reduced down the contents of the file (removing extra comments) to help make it clearer here. Here are the contents of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[www]
user = www-data
group = www-data

listen = 127.0.0.1:9000

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

clear_env = no

env[VAULT_KEY] = $VAULT_KEY
env[VAULT_TOKEN] = $VAULT_TOKEN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While most of this configuration is default settings, there are a few things to note here, starting with the &lt;code&gt;clear_env&lt;/code&gt; line. PHP-FPM, by default, will no import any environment variables that were set when the process started up. This &lt;code&gt;clear_env&lt;/code&gt; setting being set to &lt;code&gt;no&lt;/code&gt; tells it to import those values and make them accessible to Nginx. In the next few lines, there are a few values that are manually defined with the &lt;code&gt;env[]&lt;/code&gt; directive. These are variables that come from the environment and are then passed along to the PHP process as &lt;code&gt;$_ENV&lt;/code&gt; values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're paying attention, you might notice how things are starting to line up between the configuration files and how environment variables are being passed around.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This configuration will be copied into place by Compose to the &lt;code&gt;/usr/local/etc/php-fpm.d/www.conf&lt;/code&gt; path. With this file in place, we get to the last piece of the puzzle: the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;.env&lt;/code&gt; configuration (Environment variables)
&lt;/h3&gt;

&lt;p&gt;One of the handy things about Docker Compose is its ability to read from a default &lt;code&gt;.env&lt;/code&gt; file when the build/up commands are run and automatically import them. In this case we have a few settings that we don't want to hard-code in the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration and don't want to hard-code in our actual PHP code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the key to seal/unseal the Vault&lt;/li&gt;
&lt;li&gt;the token used to access the Vault API&lt;/li&gt;
&lt;li&gt;the key used for the encryption of configuration values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can define these in our &lt;code&gt;.env&lt;/code&gt; file in the base &lt;code&gt;project1/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VAULT_KEY=[ key to use for locking/unlocking ]
VAULT_TOKEN=[ token to use for API requests]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Obviously, you'll want to replace the &lt;code&gt;[...]&lt;/code&gt; strings with your values when creating the files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE: DO NOT use the root token and key in a production environment.&lt;/strong&gt; Using it here is only for example purposes without having to get into further setup and configuration on the Vault instance of other credentials to use. For more information about authentication method options in Vault, &lt;a href="https://www.vaultproject.io/docs/auth/index.html"&gt;check out this page&lt;/a&gt; in their manual.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the tricky things to note here is that, when you (re)build the Vault container, it starts from scratch and will drop any users you've created (and even reset the root key/token). The key here is to grab these values once the environment is built, put them into the &lt;code&gt;project1/.env&lt;/code&gt; and then rebuild the &lt;code&gt;php&lt;/code&gt; service to pull the new environment values in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose build -d php
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  It's all about the code
&lt;/h2&gt;

&lt;p&gt;Alright, now that we've worked through the four configuration files needed to set up the environment we need to talk about code. In this case, it's the PHP code that lives in &lt;code&gt;project1/code/&lt;/code&gt;. Since we're going to keep this super simple, the example will only have one file: &lt;code&gt;index.php&lt;/code&gt;. The basic idea behind the code is to be able to extract secrets values from the Vault server that we'll need in our application. Since we're going to use the &lt;a href="https://github.com/psecio/vaultlib"&gt;psecio/vaultlib&lt;/a&gt; library, we need to install it via Composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require psecio/vaultlib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you run that on your local system in the &lt;code&gt;project1/code/&lt;/code&gt; directory, it will set up the &lt;code&gt;vendor/&lt;/code&gt; directory with everything you need. Since the &lt;code&gt;code/&lt;/code&gt; directory is mounted as a volume on the &lt;code&gt;php&lt;/code&gt; service, it will pull it from the local version when you make the web request.&lt;/p&gt;

&lt;p&gt;With this installed, we can then initialize our Vault connection and set our first value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'VAULT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://vault:8200'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;\Psecio\Vaultlib\Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$baseUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// If the vault is sealed, unseal it&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;isSealed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;unseal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'VAULT_KEY'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Now set our secret value for "my-secret"&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my-secret'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'testing1'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Result: '&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;var_export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now if you make a request to the local instance on port &lt;code&gt;8080&lt;/code&gt; and all goes well, you should see the message "Result: true". If you see exceptions there might be something up with the container build. You can use &lt;code&gt;docker-compose down&lt;/code&gt; to destroy all of the current instances and then &lt;code&gt;docker-compose build; docker-compose up&lt;/code&gt; to bring them all back up. If you do this, be sure to swap out the Vault token and key and rebuild the &lt;code&gt;php&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;In the code above we create an instance of the &lt;code&gt;Psecio\Vaultlib\Client&lt;/code&gt; and pass in our token pulled from an environment variable. This variable exists because of a few special lines in our configuration file. Here's the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The values are set in the &lt;code&gt;.env&lt;/code&gt; file for Docker to pull in.&lt;/li&gt;
&lt;li&gt;Those values are pushed into the &lt;code&gt;php&lt;/code&gt; container using the &lt;code&gt;environment&lt;/code&gt; section in the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration.&lt;/li&gt;
&lt;li&gt;The PHP-FPM configuration then imports the environment variables and makes them available for use in the &lt;code&gt;$_ENV&lt;/code&gt; superglobal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These secrets exist in-memory in the containers and don't have to be written to a file inside of the container itself where they could potentially be compromised at rest. Once the Docker containers have started up, the &lt;code&gt;.env&lt;/code&gt; file can be removed without impacting the values inside of the containers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The tricky part here is that, if you remove the &lt;code&gt;.env&lt;/code&gt; file once the containers are up and running, you'll need to put it back if there's ever a need to run the &lt;code&gt;build&lt;/code&gt; command again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  But why is this good?
&lt;/h2&gt;

&lt;p&gt;I started this article off by giving examples of a few methods you could use for secret storage when Docker is in use but they all had rather large downsides. With this method, there's a huge plus that you won't get with the other methods: the secrets defined in the &lt;code&gt;.env&lt;/code&gt; file will only live in-memory but are still accessible to the PHP processes. This provides a pretty significant layer of protection for them and makes it more difficult for an attacker to access them directly.&lt;/p&gt;

&lt;p&gt;I will say one thing, however. Much like the fact that nothing is 100% secure, this method isn't either. It does protect the secrets by not requiring them to be sitting at rest somewhere but it doesn't prevent the &lt;code&gt;$_ENV&lt;/code&gt; values from being accessed directly. If an attacker were able to perform a remote code execution attack - tricking your application to run their code - they would be able to access these values. &lt;/p&gt;

&lt;p&gt;Unfortunately, because of the way that PHP works there's not a very good built-in method for protecting values. That's why Vault is included in this environment. It's designed specifically to store secret values and protect them at rest. By only passing in the token and key to access it, we're reducing the risk level of the system overall. Vault also includes controls to let you fine-tune the access levels of your setup. This would allow you to do something like creating a read-only user your application can use. Even if there was a compromise, at least your secret values would be protected from change.&lt;/p&gt;

&lt;p&gt;Hopefully, with the code, configuration and explanation I've provided here, you have managed to get an environment up and running and can use it to test out your own applications and secrets management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.vaultproject.io/"&gt;Vault&lt;/a&gt; homepage&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/psecio/vaultlib"&gt;psecio\vaultlib package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/"&gt;Docker.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/"&gt;Docker compose manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; homepage&lt;/li&gt;
&lt;li&gt;&lt;a href="https://php-fpm.org/"&gt;PHP-FPM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>docker</category>
      <category>secrets</category>
    </item>
    <item>
      <title>Keeping Credentials Secure in PHP</title>
      <dc:creator>Chris Cornutt</dc:creator>
      <pubDate>Thu, 04 Jul 2019 15:16:06 +0000</pubDate>
      <link>https://dev.to/enygma/keeping-credentials-secure-in-php-1mgo</link>
      <guid>https://dev.to/enygma/keeping-credentials-secure-in-php-1mgo</guid>
      <description>&lt;p&gt;** &lt;a href="https://websec.io/2018/06/14/Keep-Credentials-Secure.html"&gt;Previously posted&lt;/a&gt; on my site, Websec.io&lt;/p&gt;

&lt;p&gt;One of the most difficult things in any kind of application (not just web applications) is how to protect "secret" values. These values might be API keys, database passwords or even special bypass codes. Ideally, you're not having to define these directly in the application and can have them loaded from another source.&lt;/p&gt;

&lt;p&gt;While a lot of the issues around protecting secrets can be removed by better secret handling, it seems like there's still always a need for some kind of secret value to exist in an application. Using this sort of pattern is, obviously, recommended against. The Common Weakness Enumeration database even has an entry specifically about it: &lt;a href="https://cwe.mitre.org/data/definitions/798.html"&gt;CWE-798&lt;/a&gt;. Hard-coding credentials, especially plain-text ones, can be a huge risk if an attacker were able to somehow access the code and read them directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what about PHP?
&lt;/h2&gt;

&lt;p&gt;In PHP applications there's a common pattern to keep configuration values and access details in a &lt;code&gt;.env&lt;/code&gt; file that resides in a place where the PHP application can reach it. Given that this is a common practice, I didn't want to stray too far away from it. I want to provide something useful here that can easily replace this setup and still keep things as simple as possible.&lt;/p&gt;

&lt;p&gt;I'm going to go through several methods of credential storage, spend some time talking about what it is and the good and bad about it. They're all going to make use of simple storage methods, either based in the code or in a related flat file (like a &lt;code&gt;.env&lt;/code&gt;). We'll start off with the worst possible method - storing the plain-text credentials in the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting credentials in the code
&lt;/h3&gt;

&lt;p&gt;It's very easy as a developer to think that since you need the credentials to, say, make a connection via a HTTP client to an API that keeping the credentials as close to this code as possible is the easiest and best solution. There are two big things wrong with this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;These hard-coded credentials will now exist in your version control. If it's on a public GitHub repository, you basically just exposed those credentials to anyone that can clone that repository.&lt;/li&gt;
&lt;li&gt;If an attacker was ever able to access the source code for your application, they would have direct access to the secrets without having to do any kind of decryption or reversing work.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT:&lt;/strong&gt; If you have creds hard-coded in your files, refactor them out immediately. This is a bad security practice (and is mentions in both &lt;a href="https://cwe.mitre.org/data/definitions/256.html"&gt;CWE-256&lt;/a&gt;) and the OWASP Top 10 (as A2) in the &lt;a href="https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication"&gt;Broken Authentication&lt;/a&gt; item.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There have been several instances over the past several years, some with web applications and some with other kinds of apps/hardware, where default or hard-coded passwords were their downfall. &lt;strong&gt;AVOID this at all costs!&lt;/strong&gt; There is a very, very &lt;em&gt;very&lt;/em&gt; slim use case for having any kind of sensitive information directly in your code. Even then, there are usually other protection methods built in to ensure that those can only be used in a small number of circumstances.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.env&lt;/code&gt; file living in the document root
&lt;/h3&gt;

&lt;p&gt;So, now that we've determined that we need to avoid hard-coded, plain text credentials in your code we need to figure out another way to store them. This is where the popular &lt;code&gt;.env&lt;/code&gt; file comes in. Ever since the more modern age of PHP development has come around (thanks to tools like &lt;a href="https://getcomposer.org"&gt;Composer&lt;/a&gt;) many frameworks and libraries have adopted the pattern of using a &lt;code&gt;.env&lt;/code&gt; file to store application-specific settings. Naturally, this meant that eventually secrets and sensitive information found it's way in there. &lt;/p&gt;

&lt;p&gt;Having all of this in a separate file is better than having it hard-coded but there are still some issues. If you re-read the title of this section, you might find the issue. Remember, anything that's inside of your document root is directly readable by the outside world. In this case, the choice was made to put the &lt;code&gt;.env&lt;/code&gt; file inside of the document root. That means that I could hit &lt;code&gt;http://mycoolsite.com/.env&lt;/code&gt; and be able to access this file directly.&lt;/p&gt;

&lt;p&gt;So, we can strike this one off the list as far as a method for storing anything we need to be protected. There's another similar option, however, that can prevent direct access: moving the &lt;code&gt;.env&lt;/code&gt; file out of the document root.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.env&lt;/code&gt; file living outside of the document root
&lt;/h3&gt;

&lt;p&gt;In this case, we're moving the file up one level so it can't be directly accessed from the web. For example, if your document root is &lt;code&gt;/var/www/mycoolsite/public&lt;/code&gt; then you can move the file up one level at &lt;code&gt;/var/www/mycoolsite/.env&lt;/code&gt;. This makes it so that PHP can still access the file but it can't be reached via the web.&lt;/p&gt;

&lt;p&gt;For example, we could use the popular &lt;a href="https://github.com/vlucas/phpdotenv"&gt;vlucas/phpdotenv&lt;/a&gt; package to read the file and automatically import it into the current environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
require_once __DIR__.'/../vendor/autoload.php';

$dotenv = new Dotenv\Dotenv(__DIR__.'/../');
$dotenv-&amp;gt;load();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this script, the code is told to load the &lt;code&gt;.env&lt;/code&gt; file from one directory up (&lt;code&gt;__DIR__.'/../'&lt;/code&gt;). It then pulls in the key/value combinations and puts them into the &lt;code&gt;$_ENV&lt;/code&gt; superglobal. This method is better than having the file publicly accessible but there are also some downsides to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If an attacker is able to upload a PHP file and execute code, they could just print out the &lt;code&gt;$_ENV&lt;/code&gt; values and have direct access.&lt;/li&gt;
&lt;li&gt;The values still exist on disk in plain-text so if a local file include issue is found the file can still be read.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So this method is a step in the right direction, but we still could use something a bit more robust to protect our credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encrypted credentials
&lt;/h3&gt;

&lt;p&gt;The next step in preventing direct access to the secret values is to use either a method of obfuscation or encryption to protect the value. Since we'll need the plain-text version of the value to actually use it, obfuscation is out of the question. Using encryption we can encrypt the value and then decrypt it when needed.&lt;/p&gt;

&lt;p&gt;We're going to build on the previous example and put the values in a &lt;code&gt;.env&lt;/code&gt; file located outside of the document root. In order to help make the encryption/decryption process simpler, we're going to make use of the &lt;a href="https://github.com/defuse/php-encryption"&gt;defuse/php-encryption&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;First, we need to install it and generate a key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require defuse/php-encryption
vendor/bin/generate-defuse-key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will give a key that contains upper and lowercase letters and numbers and has sufficient entropy to be used for this simple operation. This key will need to be stored where PHP can access it but not someplace in (or even close to) the document root of the application. A common practice is to put it somewhere under &lt;code&gt;/usr/local&lt;/code&gt; in a flat file. This file then needs the permissions and owner/group changed so that PHP can read it.&lt;/p&gt;

&lt;p&gt;Once you've set up that file, you can then read and decrypt the values from the &lt;code&gt;.env&lt;/code&gt; file. We'll use the same &lt;code&gt;vlucas/phpdotenv&lt;/code&gt; library to read in the file and then &lt;code&gt;php-encryption&lt;/code&gt; to decrypt it. First the example &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test=def502003cbef858698bc40b2b8d0ffb6f365f2cef00009047650910941da72372313c7ce3f9d4ce8ba2cd64f6a5a5a330da47151c5c90124fd4e8ea792d40810d8906b8a888b12db78f1cbb0819825447ce685b1c608dfb1f30
test1=def502005c647492189c68d7f5fec781a0e10bdee8865b23f729b080c7bbadd2204005367ea6464d75609ea48be235886cd2f398bf60eaa0a0bb32e2906ab9b9b1f66c58fdd24f054b5311460fdf8770c5d729b3c296cb5d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then the PHP to decrypt it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
require_once __DIR__.'/../vendor/autoload.php';

$dotenv = new Dotenv\Dotenv(__DIR__.'/../');
$dotenv-&amp;gt;load();

$keyContents = file_get_contents('/usr/local/keyfile`);
$key = \Defuse\Crypto\Key::loadFromAsciiSafeString($keyContents);

$secret = \Defuse\Crypto\Crypto::decrypt($ciphertext, $key);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our decrypted secret value then ends up in the &lt;code&gt;$secret&lt;/code&gt; variable. Obviously, you wouldn't want to have to copy and paste this code all around so it'd be simpler to wrap it in a helper function or class to make it more self-contained.&lt;/p&gt;

&lt;p&gt;This is yet another step in the right direction in protecting our credentials but there's still an issue here that's common to all of these flat-file storage methods: the local file include. If an attacker is able to make your code read and expose file contents, that means it could not only read the encrypted values from the &lt;code&gt;.env&lt;/code&gt; but also the contents of the &lt;code&gt;keyfile&lt;/code&gt; since PHP needs to be able to read that too.&lt;/p&gt;

&lt;p&gt;We're running out of options here but let me suggest one more. This method still allows you to store the values in a flat-file but protects them from local file include attacks as PHP doesn't need to be able to access the file they're contained in, only the Apache web server. Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Apache Pull" Method
&lt;/h2&gt;

&lt;p&gt;In this method, we're going to use some of the same kind of techniques as before (storing the secrets encrypted in a flat-file) but there's a new twist: making use of Apache environment variables to relay those values to PHP.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This tutorial shows how to set up an Apache web server but this same approach can probably be performed via Nginx as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want the quick and easy version, I've already set up &lt;a href="https://github.com/psecio/protected-env-example"&gt;this repository&lt;/a&gt; with a Docker-based example showing how the environment needs to be configured.&lt;/p&gt;

&lt;p&gt;Here are the steps the code and applications will follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A file is created containing the encrypted credentials somewhere on the file system (in this case we're just putting it in &lt;code&gt;/tmp&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;This file is then sourced in the &lt;code&gt;/etc/apache2/envvars&lt;/code&gt; file as an additional source pulling in these values as local environment variables.&lt;/li&gt;
&lt;li&gt;When Apache starts up it pulls in all of the values from &lt;code&gt;envvars&lt;/code&gt; and redefines them internally. This includes our special values.&lt;/li&gt;
&lt;li&gt;These values are pushed out to PHP via Apache environment variables through a &lt;code&gt;SetEnv&lt;/code&gt; statement.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The question you may be asking now has to do with those pesky local file include issues. Can't the additional settings still be read by PHP? That's where the last piece of the puzzle comes in: the &lt;code&gt;open_basedir&lt;/code&gt; configuration. With PHP, you can use &lt;code&gt;open_basedir&lt;/code&gt; to set the directories that PHP can interact with and keep it from going outside of those. In this case, we can lock PHP down to just the document root and prevent it from reaching out and getting the file manually.&lt;/p&gt;

&lt;p&gt;Before I go on and show how it works, I do want to say one thing - this solution isn't perfect either. If an attacker were able to execute PHP code and read from the &lt;code&gt;$_ENV&lt;/code&gt; superglobal, the key value would still be exposed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Secrets
&lt;/h3&gt;

&lt;p&gt;First, we'll set up the secrets and get them sourced correctly. In our &lt;code&gt;/tmp/addl-settings&lt;/code&gt; file, we've defined the key value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ENC_KEY=1234567890 // This is just a sample key, obviously
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we source this file in the &lt;code&gt;envvars&lt;/code&gt; file at the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. /tmp/addl-settings
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When this is set up, Apache will then load the &lt;code&gt;ENC_KEY&lt;/code&gt; value into its internal environment and make it available.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Apache Config
&lt;/h3&gt;

&lt;p&gt;Next up is the Apache configuration. In this case, we're going to make use of virtual hosts but you could also do this at the base level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    SetEnv ENC_KEY ${ENC_KEY}
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this configuration you can see the special &lt;code&gt;${}&lt;/code&gt; notion that's used to pull an Apache environment variable in and make it available to the running process. In PHP this means making it a value in &lt;code&gt;$_ENV&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open_basedir
&lt;/h3&gt;

&lt;p&gt;The final step of the puzzle is set up the &lt;a href="http://php.net/manual/en/ini.core.php#ini.open-basedir"&gt;open_basedir&lt;/a&gt; protection so we create an &lt;code&gt;open_basedir.ini&lt;/code&gt; file and copy it to the right place for Apache to read it as a PHP ini configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open_basedir=/var/www/html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With all of this in place, the &lt;code&gt;ENC_KEY&lt;/code&gt; value - our encryption key - is now available to PHP via Apache but cannot be accessed directly as a file.&lt;/p&gt;

&lt;h2&gt;
  
  
  But wait, there's more!
&lt;/h2&gt;

&lt;p&gt;This setup is great and all but you might be asking yourself "How to I read my encrypted configuration settings now"? Well, with the help of a handy library - &lt;a href="https://github.com/psecio/secure_dotenv"&gt;psecio/secure_dotenv&lt;/a&gt; that takes care of a lot of the processing, it's much simpler. We already have the key we need for encryption and decryption in the environment so we'll just reuse that for these examples. First, install the package using &lt;a href="https://getcomposer.org"&gt;Composer&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require psecio/secure_dotenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, in your application, create the new &lt;code&gt;Parser&lt;/code&gt; instance feeding in the environment variable with the path to the key file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
$envFile = __DIR__.'/.env';
$parser = new \Psecio\SecureDotenv\Parser($_ENV['ENC_KEY'], $envFile);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Reusing the &lt;code&gt;.env&lt;/code&gt; file from the above examples gives us values for &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;test1&lt;/code&gt; which can be extracted from the result of a &lt;code&gt;getContent&lt;/code&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
echo 'test1 is: '.$parser-&amp;gt;getContent()['test1'];
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The library handles the decryption of the value for you behind the scenes (using the same &lt;a href="https://github.com/defuse/php-encryption"&gt;defuse/php-encryption library&lt;/a&gt;) and you're left with a plain-text result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Securing secrets in PHP applications is an interesting problem to tackle. In the research I did prior to this article, I found that - much like any other security-related topic - there's always more than one way to accomplish a task. PHP makes it even more difficult because of how it interacts with web servers. The PHP scripts and processing need to have at the least read access to every file they need to work with. This makes it &lt;em&gt;very&lt;/em&gt; difficult to prevent local file include issues if you're not very careful.&lt;/p&gt;

&lt;p&gt;There aren't any 100% secure options for credential storage but this "Apache pull" method I've shared here is one of the simpler methods that doesn't require much more than the technology you're already using. Of course, if you have a more complex environment that's deployed using Chef, Vagrant or other tools, those come with some additional features (like encrypted Chef databags) that can be used for credential handling as well.&lt;/p&gt;

&lt;p&gt;Remember, there's no "one size fits all" solution for this. It depends a lot on your environment and the risk requirements for your application. Be sure to sit down and create an accurate &lt;a href="https://www.owasp.org/index.php/Application_Threat_Modeling"&gt;threat model&lt;/a&gt; of your application before making a decision on how you're going to protect your secrets. This will give you a better overall picture of your needs and what kind (or kinds) of protection you'll need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/defuse/php-encryption"&gt;defuse/php-encryption&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/psecio/secure_dotenv"&gt;psecio/secure_dotenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/en/ini.core.php#ini.open-basedir"&gt;open_basedir&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vlucas/phpdotenv"&gt;vlucas/phpdotenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/psecio/protected-env-example"&gt;An example of the "Apache pull" protected environment&lt;/a&gt; (uses Docker)&lt;/li&gt;
&lt;li&gt;Other credential storage options: &lt;a href="https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3cd"&gt;https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3cd&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>credentials</category>
    </item>
  </channel>
</rss>
