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:
- Commit
composer.lock. Always, for applications. - Use
composer installin CI, notcomposer update.composer installreads the lockfile.composer updateresolves fresh versions. - Review lockfile changes in pull requests.
- Use
--no-devin 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.