Steve Grunwell

Open-source contributor, speaker, and coffee snob

A wall full of different keys

Handling Credentials in Laravel

I was recently asked to code review a friend’s first Laravel app, and when I cloned the repository from GitHub I immediately noticed a few big, red flags. Many of these were common mistakes, so I thought I’d take a moment to discuss how we can safely handle credentials and/or sensitive information in our Laravel applications.

Understanding the .env file

Laravel makes use of the phpdotenv library to store environment-specific credentials in a .env file in the root of the project.

An example .env file might look something like this:

When Laravel is bootstrapping, it checks to see if an .env file is present and, if so, reads those values into the process environment.

.env files are extremely useful during development, as each developer will have their own copy with slightly-different configurations, tuned to their own environments.

It’s important that .env files are never checked into source control! Your .env file should be the single place where all API keys, passwords, etc. are stored — treat it accordingly!

In my friend’s case, he was storing the .env file in the Git repository, meaning I had access to his credentials as soon as I cloned the repo. Fortunately they were only development credentials, but there were still a few API keys that needed rotating.

Helping other developers with .env.example

Since we’re not committing .env files into source control, it’s common practice to include a .env.example file in the repository that contains the necessary information for a new developer on the project to get started.

This example file should not contain any sensitive information, but should have empty environment variables so the new developer knows what might need to be filled in.

If there are non-standard configuration values, it’s also helpful to include explanations and/or instructions to help developers get started:

Pro-tip: Automatically copy .env.example to .env

Here’s a nice one-liner that I include in all of my Laravel applications’ composer.json file:

This adds a post-install-cmd script that automatically checks for the presence of an .env file in the local copy of the project. If one doesn’t exist, .env.example is copied to .env, then Composer automatically runs php artisan key:generate, which generates the unique application key and writes it to APP_KEY in the newly-created file.

How does Laravel’s config/ directory work?

One of my favorite “so beautiful in its simplicity” features within Laravel is its method of handling application configuration: each file in config/ returns an array, which can be nested as much as we need them to be.

For example, config/database.php looks something like this:

I’ve truncated the file quite a bit for readability, but there are a few important points:

First, this file is config/database.php, which will become available to us within our app as config('database'). Similarly, we could create config/steve.php and it would be available at config('steve').

We can further access values with the config() helper and “dot”notation”; to retrieve the value of ['connections']['mysql']['url'], we can call config('database.connections.mysql.url').

Next, notice that we’re using the env() helper, which basically says “find the environment variable with this name and return it’s value; if we can’t find it, return a default value (or null).”

This combination can be really powerful: when Laravel needs to determine what the default database engine is, it might call config('database.default') — if the .env file has declared a value for DB_CONNECTION, then that value will be used; otherwise, Laravel will use “mysql”.

Avoid parsing environment variables outside of the configuration

A word of warning: in production environments, it’s recommended to cache configuration values rather than re-read them from the environment upon every request.

When such a cache file is present, Laravel will not attempt to parse the .env file, which can lead to unexpected results.

For example, my friend’s application included the following line in the app’s main layout file:

In development, this was fine, and his pages would render the value of the APP_NAME environment variable in the <title /> element of each page.

Were he to deploy this to production and enable configuration caching, however, the app name would start coming back empty because his .env file would no longer be parsed!

Instead, the call to env('APP_NAME') should be replaced with config('app.name'), which reads the “name” key from config/app.php.

The right place to store third-party credentials

It’s not uncommon for Laravel applications to interface with third-party [micro-]services and APIs, and Laravel provides a stock configuration file for this very purpose: config/services.php.

From the inline documentation at the top of that file:

This file is for storing the credentials for third party services such as Mailgun, Postmark, AWS and more. This file provides the de facto location for this type of information, allowing packages to have a conventional file to locate the various service credentials.

Before you rush to create, for example, config/geocoding.php with just the public/private keypair from our example above, consider adding these credentials to config/services.php:

With this array defined, we can access these configuration values as needed throughout our codebase through calls like config('services.geocoding.endpoint').

You’ll notice that we’re using the env() helper in two ways:

  1. If the EXAMPLECOM_GEOCODING_API_ENDPOINT environment variable is set, use that value. Otherwise, default to “https://example.com/api”.
  2. For the API credentials, we’re not providing default values; if the developer hasn’t configured these keys, we want the requests to fail.
    • The integration with the third-party API should make clear that missing API credentials are to blame for the failed request.

Summary

A lot of the mistakes my friend made are pretty common, especially for developers just getting started with application development (regardless of framework). Heed the following advice to keep your secrets…well, secret:

  1. Keep sensitive information — application keys, API keys, passwords, etc. — out of version control.
    • Add the relevant file(s) to your .gitignore file to ensure nobody else can check them in, either
  2. Provide an .env.example file that tells other developers where to get the credentials they might not already have.
    • This might be a service they need to sign up for, someone in the organization they need to talk to, and/or a secure place (such as a password vault) where they need to look.
  3. Avoid using env() outside of the config/ directory; instead, define configuration values based on the environment variables and reference those values throughout the codebase using the config() helper.
  4. Store references to third-party services within config/services.php to make it clear at a glance which services are being used and set reasonable defaults without exposing sensitive information.

Previous

Building a Quick, Private Family Blog with WordPress

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Be excellent to each other.