IT Vendor Management: Getting Value From Your IT Suppliers

10 min read 1,927 words
IT Vendor Management: Getting Value from Your IT Suppliers Without Being Taken Advantage Of featured image

Why Hardcoding Secrets Creates Real Security Risks

When developers embed database credentials, API keys, or service passwords directly into PHP files, those secrets end up in version control history. Even after you remove them from the current code, the secrets remain in git commits, backup files, and deployment artifacts. Anyone with access to the repository can retrieve them.

Environment variables solve this problem by loading sensitive configuration at runtime rather than storing it in source code. This approach keeps secrets out of version control and lets you use different values for development, staging, and production environments without changing a single line of application code.

What Environment Variables Are and How They Work

Environment variables are key-value pairs that the operating system makes available to running processes. When PHP starts, it inherits these variables from the web server or command line environment. Your application reads them at runtime instead of relying on hardcoded values.

A typical environment variable looks like this:

DB_PASSWORD=your_secure_password_here

PHP accesses these variables through the getenv() function or the $_ENV superglobal. The $_ENV superglobal provides direct access to environment variables as an associative array, which often makes code more readable.

// Reading a single environment variable
$dbPassword = getenv('DB_PASSWORD');

// Using $_ENV superglobal
$dbPassword = $_ENV['DB_PASSWORD'] ?? null;

Setting Environment Variables on Different Platforms

Setting Variables on Linux and macOS

On Linux or macOS servers, you can set environment variables in several ways. For Apache, you might add them to a virtual host configuration:

SetEnv DB_HOST "localhost"
SetEnv DB_NAME "myapp"
SetEnv DB_USER "app_user"
SetEnv DB_PASSWORD "secure_password_here"

For Nginx with PHP-FPM, you can set environment variables in the PHP-FPM pool configuration:

env[DB_HOST] = localhost
env[DB_NAME] = myapp
env[DB_USER] = app_user
env[DB_PASSWORD] = secure_password_here

For command-line scripts and cron jobs, exporting variables directly works well:

export DB_PASSWORD="secure_password_here"
php script.php

Setting Variables on Windows

On Windows servers using IIS, you can configure environment variables through the IIS Manager. Navigate to your application pool, open Advanced Settings, and add environment variables under Process Model. This approach keeps secrets separate from your application code while making them available to PHP processes.

Using .env Files for Local Development

Many PHP developers use .env files for local development because they are easy to manage and fit into typical developer workflows. A .env file in your project root contains key-value pairs:

DB_HOST=localhost
DB_NAME=myapp
DB_USER=app_user
DB_PASSWORD=secure_password_here
API_KEY=your_api_key_here
MAIL_PASSWORD=smtp_password

You then need a PHP library to load these values. The most common approach uses a library like vlucas/phpdotenv, which reads the .env file and loads variables into the environment:

composer require vlucas/phpdotenv

After installation, load the environment at the start of your application entry point:

require_once __DIR__ . '/vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

The createImmutable method ensures variables cannot be changed after loading, which prevents accidental overrides during runtime. If your project structure places the .env file in the project root rather than the same directory as the script, adjust the path accordingly:

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();

Reading Environment Variables in Your Application

Once environment variables are loaded, your application reads them just like any other configuration source. A database connection class might look like this:

class Database {
    private string $host;
    private string $dbname;
    private string $username;
    private string $password;

    public function __construct() {
        $this->host = $_ENV['DB_HOST'] ?? 'localhost';
        $this->dbname = $_ENV['DB_NAME'] ?? 'myapp';
        $this->username = $_ENV['DB_USER'] ?? 'root';
        $this->password = $_ENV['DB_PASSWORD'] ?? '';
    }

    public function connect(): PDO {
        $dsn = "mysql:host={$this->host};dbname={$this->dbname}";
        return new PDO($dsn, $this->username, $this->password);
    }
}

The null coalescing operator ?? provides default values when environment variables are not set. This prevents errors during development when some variables might be missing, though you should ensure production environments have all required variables configured.

Why .env Files Should Stay Out of Version Control

The .env file contains sensitive data, which means it should never be committed to version control. Add it to your .gitignore file:

# Environment variables
.env
.env.local
.env.*.local

Instead, commit a .env.example file that contains placeholder values. This file shows other developers which environment variables the application needs without exposing actual secrets:

DB_HOST=localhost
DB_NAME=myapp
DB_USER=app_user
DB_PASSWORD=
API_KEY=

Each developer then copies .env.example to .env and fills in their own values. This approach works well for teams because it documents the required configuration without creating security risks.

Common Mistakes When Using Environment Variables

Many developers make the mistake of only protecting production secrets while leaving development environments insecure. This creates risk because development environments often connect to real services with limited permissions that attackers can still exploit.

Another common mistake is logging environment variables. When debugging code, it is tempting to output all configuration values to see what is loaded. This exposes secrets in log files that may be stored indefinitely, shared with third parties, or exposed through monitoring systems.

A third mistake involves storing environment variables in configuration files that get committed accidentally. Always verify your .gitignore file includes .env before pushing to a repository, especially when setting up a new project.

Handling Environment Variables in Different PHP Frameworks

Laravel handles environment variables automatically through its .env file and configuration caching system. The framework reads .env on boot and makes variables available through the env() helper function:

$apiKey = env('API_KEY');

Symfony uses a similar approach with its .env.dist file as a template. The framework recommends against using $_ENV directly and instead uses the dependency injection container to access configuration values.

If you are working with a custom application without a framework, consider using a library like phpdotenv as shown earlier. This gives you the convenience of .env files while keeping your code portable across different hosting environments.

Security Considerations for Environment Variables

Environment variables reduce the risk of secrets appearing in version control, but they are not a complete security solution on their own. Server processes that read environment variables may expose them through process listings or error messages. On shared hosting environments, other users on the same server might access environment variables through /proc/self/environ.

For production environments, consider using a secrets manager. Services like AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault store sensitive credentials securely and provide them to your application at runtime. These tools often integrate with PHP through SDKs or APIs, giving you a more robust approach than environment files alone.

Regardless of how you store secrets, rotate them regularly. If a credential is exposed, change it immediately and update your environment configuration. This practice limits the window of opportunity if someone gains access to old credentials.

Deploying PHP Applications With Environment Variables

When deploying to a server, you need to ensure environment variables are available to your application. On cloud platforms like AWS, Heroku, or DigitalOcean App Platform, you typically set environment variables through the hosting dashboard. These platforms inject them automatically when your application starts.

For Docker containers, you can pass environment variables through the docker-compose.yml file:

services:
  web:
    image: php:apache
    environment:
      DB_HOST: mysql
      DB_NAME: myapp
      DB_USER: app_user
      DB_PASSWORD: secure_password_here
    depends_on:
      - mysql

For production deployments, use Docker secrets or a similar mechanism to avoid storing passwords in plain text within docker-compose files. In production, environment variables are usually provided by the container orchestration system rather than hardcoded in configuration files.

Testing Code That Uses Environment Variables

Testing code that depends on environment variables requires careful setup to ensure tests run consistently regardless of the server environment. In PHPUnit, you can use the @env annotation or set environment variables in a bootstrap file:

protected function setUp(): void {
    $_ENV['DB_HOST'] = 'localhost';
    $_ENV['DB_NAME'] = 'test_db';
    $_ENV['DB_USER'] = 'test_user';
    $_ENV['DB_PASSWORD'] = 'test_password';
}

Many developers prefer using dependency injection to make configuration explicit. By passing configuration values through constructors rather than reading them from $_ENV, you can mock configuration during tests more easily:

class Database {
    public function __construct(
        private string $host,
        private string $dbname,
        private string $username,
        private string $password
    ) {}

    public function connect(): PDO {
        $dsn = "mysql:host={$this->host};dbname={$this->dbname}";
        return new PDO($dsn, $this->username, $this->password);
    }
}

// In tests, pass mock values directly
$db = new Database('localhost', 'test_db', 'test_user', 'test_password');

This approach decouples configuration from environment reading, making your code more testable and your intentions clearer.

Related practical reading

These related guides can help you connect this topic with the wider website, server, security, and support decisions around it.

Moving Forward With Environment Variables

Separating configuration from code is one of the foundational practices for building secure PHP applications. Environment variables give you a clean way to manage credentials and sensitive settings across different environments without exposing them in version control.

Start by auditing your current codebase for hardcoded secrets. Move them to a .env file for local development and ensure your production environment loads them through your hosting platform or deployment system. If you are working on a larger application, consider using a secrets manager for added protection.

Good security practices around configuration are worth investing in early. The effort to set up environment variables properly pays off in reduced risk and easier maintenance as your application grows.

Frequently Asked Questions

Can I use environment variables with shared hosting?
Shared hosting environments often have limited options for setting environment variables. Some providers allow you to set variables through their control panel or .htaccess file. You can also use PHP's putenv() function to set variables at runtime, though this requires loading a .env file manually. Check with your hosting provider to see what methods they support.
Should I use $_ENV or getenv() in PHP?
Both work for reading environment variables, but they behave slightly differently. $_ENV provides direct array access, which many developers find more readable. getenv() can optionally accept a second parameter to return false instead of false as a string if the variable does not exist. $_ENV is generally preferred for its simplicity, but either approach works if used consistently.
How do I handle missing environment variables gracefully?
Use the null coalescing operator ?? to provide default values or throw an exception when required variables are missing. For critical configuration like database credentials, it is often better to fail explicitly with a clear error message than to continue running with incomplete configuration:
Is it safe to commit .env.example files?
Yes, .env.example files should be committed because they document which environment variables your application needs. Include the variable names but leave values empty. This helps other developers understand your configuration without exposing any secrets.
How do environment variables compare to PHP configuration files?
Environment variables work across different languages and deployment targets, making them more portable than PHP-specific configuration files. They also integrate better with containerised deployments and cloud platforms. PHP configuration files give you more control over validation and type checking but tie your configuration to the PHP runtime.