Dependency Security

PHP Composer Dependency Security

Securing PHP applications through Composer lockfiles, Packagist verification, and automated vulnerability scanning.

Yukti Singhal
Security Engineer
4 min read

PHP powers a significant portion of the web, and Composer is the standard dependency manager. Packagist.org serves as the default package registry with over 350,000 packages. Like every open registry, it comes with supply chain risks that PHP developers need to manage proactively.

PHP Supply Chain Risks

PHP's ecosystem has some unique characteristics that affect supply chain security:

  • Composer plugins. Plugins can execute arbitrary code during install, update, and other Composer lifecycle events. They are a privileged attack vector.
  • Autoloading. PHP's autoloading mechanism means that including a package makes all its classes available. A malicious package can overwrite or shadow legitimate classes.
  • Legacy packages. The PHP ecosystem has a long tail of packages that predate Composer and modern security practices.
  • Packagist account security. Packagist accounts have historically been easier to compromise than accounts on registries that enforce MFA.

composer.lock Is Your Lockfile

composer.lock records exact versions and content hashes for every package:

{
    "name": "monolog/monolog",
    "version": "3.5.0",
    "source": {
        "type": "git",
        "url": "https://github.com/Seldaek/monolog.git",
        "reference": "c915e2634718c1abbe3e3fa5d9..."
    },
    "dist": {
        "type": "zip",
        "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e...",
        "reference": "c915e2634718c1abbe3e3fa5d9...",
        "shasum": ""
    }
}

Rules:

  1. Commit composer.lock. Always, for applications.
  2. Use composer install in CI, not composer update. composer install reads the lockfile. composer update resolves fresh versions.
  3. Review lockfile changes in pull requests.
  4. Use --no-dev in production builds. Development dependencies should never reach production.
composer install --no-dev --optimize-autoloader --no-interaction

Vulnerability Scanning

Local Symfony Security Checker

The Symfony Security Checker is the standard tool for auditing Composer dependencies:

composer audit

composer audit (available since Composer 2.4) checks packages against the PHP Security Advisories Database. Run it in CI:

composer audit --format=json

Roave Security Advisories

An alternative approach is requiring the roave/security-advisories package as a dev dependency:

composer require --dev roave/security-advisories:dev-latest

This meta-package conflicts with every known vulnerable package version. If you try to install a vulnerable version, Composer's dependency resolver will fail. It is an elegant solution that works without any additional tooling.

Controlling Composer Plugins

Composer 2.2+ introduced plugin restrictions. Configure allowed plugins in composer.json:

{
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "phpstan/extension-installer": true
        }
    }
}

Any plugin not in this list will be blocked. This prevents a compromised dependency from registering a malicious Composer plugin.

Packagist Security

Private Packagist

For organizations with internal packages, use Private Packagist instead of running a custom Satis mirror:

{
    "repositories": [
        {
            "type": "composer",
            "url": "https://yourcompany.repo.packagist.com"
        }
    ]
}

Private Packagist provides mirroring, access control, and vulnerability scanning.

Dependency Confusion Prevention

Disable Packagist.org for internal packages:

{
    "repositories": [
        {
            "type": "composer",
            "url": "https://yourcompany.repo.packagist.com",
            "canonical": true
        },
        {
            "packagist.org": false
        }
    ]
}

The canonical: true flag means this repository is the authoritative source. Packages found here will not be looked up on Packagist.org.

Platform Requirements

Enforce platform requirements to prevent deploying packages incompatible with your production environment:

{
    "config": {
        "platform": {
            "php": "8.2.0",
            "ext-openssl": "1.0.0"
        }
    }
}

This catches version mismatches early, before they become runtime errors or security issues from missing extensions.

Autoloader Optimization

In production, use optimized autoloading to prevent class hijacking:

composer dump-autoload --optimize --classmap-authoritative

The --classmap-authoritative flag tells PHP to only load classes from the generated classmap. If a class is not in the map, it is not loaded, even if a file on disk provides it. This prevents injected files from being autoloaded.

SBOM Generation

Generate a CycloneDX SBOM for your PHP project:

composer require --dev cyclonedx/cyclonedx-php-composer
composer make-bom --output-file=sbom.json --spec-version=1.5

Include this in your CI pipeline to produce SBOMs for every build.

How Safeguard.sh Helps

Safeguard.sh provides continuous dependency monitoring for PHP applications. It ingests CycloneDX SBOMs from your Composer builds, tracks package versions across all your services, and correlates them against vulnerability databases. When new PHP security advisories drop, Safeguard.sh identifies affected applications across your organization instantly and helps your team prioritize which services to patch first based on deployment exposure and vulnerability severity.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.