Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 23, 20264 min read

Dependency Confusion Attacks: Securing Your Node.js and PHP Supply Chains

Dependency confusion attacks can silently compromise your app. Learn how to secure your Node.js and PHP supply chains using scoped registries and lockfiles.

securitynodejsphpsupply-chaindevopspackage-managementWebBackend

During an on-call rotation last year, I spent about two days tracing a weird build failure that turned out to be a misconfigured private registry. It wasn't a malicious attack, but it highlighted exactly how fragile our package management workflows are when they rely on implicit trust.

A dependency confusion attack happens when a package manager is tricked into pulling a malicious package from a public registry (like npm or Packagist) instead of your internal, private one. If you have an internal package named @company/auth, an attacker can register that same name on a public registry with a higher version number. Your build server, seeing a "newer" version, blindly pulls the malicious code.

Understanding the Package Manager Vulnerability

The core issue is that many package managers prioritize version numbers over registry source by default. They assume that if v2.0.0 exists, it must be the "latest" and therefore the best version to install, regardless of whether it came from your private Artifactory instance or the public npm registry.

We initially tried solving this by simply deleting the public packages that collided with our names. That failed quickly because we couldn't keep up with the volume of potential name-squatting. We needed a more robust approach to supply chain security that didn't rely on manual cleanup.

Securing Node.js with Scoped Registries

In Node.js, the most effective defense is using scoped packages. When you use a scope (e.g., @mycompany/package), you can explicitly map that scope to a specific registry in your .npmrc file. This tells npm to only look for packages starting with @mycompany/ in your private registry.

Here is how you configure it:

Bash
# In your .npmrc file
@mycompany:registry=https://npm.pkg.github.com/
always-auth=true

By binding the @mycompany scope to your private URL, you eliminate the ambiguity. npm will no longer query the public registry for any package under that scope. If you aren't using scopes, your risk profile is significantly higher. If you're managing dependencies, you should also consider preventing uncontrolled resource consumption by auditing your lockfiles regularly, as dependency bloat often hides these vulnerabilities.

Hardening Composer for PHP Projects

PHP developers using Composer face a similar composer vulnerability. Composer’s default behavior is to check all configured repositories for a package and select the one with the highest version. If you have a private repo and a public one, it’s a race condition waiting to happen.

To fix this, you must use the packagist.org: false setting in your composer.json for internal packages, or use the exclude directive.

JSON
{
    "repositories": [
        {
            "type": "composer",
            "url": "https://satis.mycompany.com"
        },
        {
            "packagist.org": false
        }
    ]
}

Setting packagist.org: false prevents Composer from looking at the public registry for packages it can't find in your private ones. It’s a strict "allow-list" approach that significantly tightens your package manager security.

Best Practices for Long-Term Defense

Beyond registry configuration, you need to treat your lockfiles as immutable truth.

  1. Commit Lockfiles: Always commit package-lock.json or composer.lock. These files contain the integrity hashes (SHA-512) for every dependency. If an attacker swaps a package, the hash won't match, and the install will fail.
  2. Use Scoped Packages: If you’re writing internal libraries, always prefix them with a scope. It’s the single most effective way to prevent namespace collisions.
  3. Audit Regularly: Use tools like npm audit or composer audit to identify known vulnerabilities. While these don't prevent confusion attacks directly, they keep your overall npm security posture healthy.

I’m still not 100% comfortable relying purely on registry configuration, though. I've started experimenting with local proxy caches that strip out public registry access entirely, forcing developers to explicitly request new dependencies. It's a bit of a friction increase for the team, but in an environment where we are already preventing open redirect vulnerabilities and other common flaws, the peace of mind is worth the extra step.

Dependency confusion is a subtle threat, but once you enforce scoped registries and use your lockfiles correctly, you remove the "confusion" entirely.

Frequently Asked Questions

Does using a lockfile stop dependency confusion? Yes, mostly. If you already have the package installed and the lockfile generated, the hash will prevent the installation of a malicious, "newer" version. However, if you are installing the package for the first time, the lockfile doesn't exist yet, which is why scoped registries are your primary defense.

Is this only a problem for private packages? Yes. Public, open-source packages are generally safe from this specific confusion attack because they don't have a private mirror. However, you should still be wary of "typosquatting," where an attacker names a package lodsh instead of lodash.

Should I use an internal proxy? If you work at a large scale, using a tool like Verdaccio or Sonatype Nexus is highly recommended. It allows you to create a virtual registry that aggregates your private packages and specific, vetted versions of public packages, giving you total control over what enters your build pipeline.

Back to Blog

Similar Posts

SecurityJune 23, 20264 min read

Cache poisoning prevention: Secure your CDN and proxy layers

Cache poisoning happens when malicious headers trick your CDN. Learn how to secure your Node.js and PHP apps against header injection and request smuggling.

Read more
SecurityJune 22, 20264 min read

Preventing Open Redirect Vulnerabilities: A Guide for Developers

Learn to stop open redirect vulnerabilities by validating destination URLs. Protect your Node.js and PHP apps from phishing attacks with these practical tips.

Read more
SecurityJune 23, 20264 min read

OAuth2 Security: How to Properly Validate Redirect URIs

Master OAuth2 security by implementing strict redirect URI validation. Prevent authorization code injection and open redirects in your authentication flows.

Read more