How-To Guide

Securing Your Private Package Registry

Private package registries are high-value targets for supply chain attackers. Here is how to lock them down, from access controls to dependency confusion prevention.

Shadab Khan
Application Security Architect
6 min read

If your organization uses a private package registry, it is one of the most valuable targets in your infrastructure from an attacker's perspective. Compromise the registry, and you can inject malicious code into every application that pulls packages from it. Yet most private registries I audit have weaker security controls than the applications they serve.

This guide covers practical steps to harden your private package registry against the attack patterns that actually matter.

Why Private Registries Are Targeted

A private package registry sits at the intersection of trust and reach. Developers trust packages from the internal registry because they are "ours." And those packages are used across many applications, so compromising one package can affect dozens of services.

The primary attack vectors:

  • Credential theft: Stolen developer tokens or CI/CD service account credentials used to publish malicious packages.
  • Dependency confusion: Attackers publish packages with the same names as your internal packages on public registries, hoping your build systems pull the malicious public version.
  • Account compromise: Taking over a maintainer account to publish a poisoned update.
  • Infrastructure compromise: Direct attack on the registry server or service itself.

Access Control Architecture

Authentication

Require authentication for all operations. Even reading packages should require authentication. Anonymous read access means anyone who discovers your registry URL can enumerate your internal package names, which is the first step in a dependency confusion attack.

Use token-based authentication, not passwords. Tokens can be scoped, rotated, and revoked independently.

For npm registries:

# Generate a scoped, read-only token
npm token create --read-only

For Artifactory:

# Create an access token with specific permissions
curl -X POST "https://your-artifactory/api/security/token" \
  -d "username=ci-service" \
  -d "scope=member-of-groups:readers" \
  -d "expires_in=3600"

Authorization

Implement least-privilege access. Not every developer needs publish permissions. Structure your access in tiers:

  • Read-only: All developers and CI/CD build pipelines. They can install packages but not publish.
  • Publish to specific scopes: Team members can publish to their team's namespace only. The frontend team publishes @frontend/*, the platform team publishes @platform/*.
  • Admin: A small group that manages registry configuration, user access, and global policies.

Use scoped packages. Scoped packages (@company/package-name) provide namespace isolation and make dependency confusion attacks harder because the scope is bound to your organization.

{
  "dependencies": {
    "@company/auth-lib": "^2.1.0",
    "@company/logging": "^1.5.0"
  }
}

CI/CD Integration

Use ephemeral tokens for CI/CD. Generate a short-lived token at the start of each build and destroy it after. Do not store long-lived tokens in CI/CD secrets if your platform supports OIDC token exchange.

# GitHub Actions with OIDC
- name: Configure npm
  run: |
    npm config set //registry.company.com/:_authToken=${{ steps.auth.outputs.token }}

Separate build and publish credentials. The token used to install dependencies during builds should not have permission to publish. Use a different, more restricted credential for the publish step, and only in release pipelines.

Dependency Confusion Prevention

Dependency confusion is one of the most practical supply chain attacks, and private registries are the primary target.

How It Works

Your project depends on @company/utils. An attacker publishes a package called company-utils (without the scope) on npmjs.com with a very high version number. If your build system checks both public and private registries and prefers the higher version, it pulls the attacker's package.

Prevention Measures

Always use scoped packages. Scopes are registry-specific. @company/* resolves to your private registry, not npmjs.com.

Configure your .npmrc:

@company:registry=https://registry.company.com/
registry=https://registry.npmjs.org/

Register your scope on the public registry. Even if you do not publish there, register the @company scope on npmjs.com to prevent attackers from claiming it.

Configure upstream proxy rules carefully. If your registry proxies public registries, configure it to prefer local packages when names collide.

For Artifactory:

# In your virtual repository configuration
# Set "Priority Resolution" to prioritize local repositories over remote

Monitor public registries for your package names. Set up alerts for packages published on npmjs.com, PyPI, or other public registries that match your internal package naming patterns.

Package Integrity

Enable package signing. Sign packages on publish and verify signatures on install. This ensures packages have not been tampered with between publish and install.

Use content-addressable storage. Verify that the package content matches its declared checksum. Most registries handle this natively, but verify it is enabled.

Immutable versions. Once a version is published, it should not be overwritable. If version 1.2.3 exists, publishing 1.2.3 again should fail. This prevents attackers who gain publish access from silently replacing legitimate packages.

# Artifactory: enable "Block Redeployment" on your repository

Audit and Monitoring

Log all publish events. Every package publication should be logged with: who published, when, from where (IP, CI job), and what changed.

Alert on anomalies:

  • Publications outside business hours.
  • Publications from new IP addresses or locations.
  • Unusual package sizes (a 10 KB utility library suddenly becoming 500 KB).
  • Version jumps that skip expected semver progression.

Periodic access reviews. Quarterly, review who has publish access and revoke it for anyone who no longer needs it. People change teams, leave the company, or simply no longer work on the packages they once maintained.

Scan hosted packages. Run vulnerability scans against the packages in your registry, not just the packages your applications consume. Catch issues at the source.

Infrastructure Security

Run the registry in a private network. Your package registry should not be directly internet-accessible. Developers and CI/CD systems should access it through VPN or private network connectivity.

Enable TLS everywhere. All communication with the registry must be encrypted. Configure certificate pinning for CI/CD systems.

Back up regularly. Package registries are critical infrastructure. If yours goes down, builds stop. Maintain regular backups and test recovery procedures.

Keep the registry software updated. Artifactory, Nexus, Verdaccio, and other registry platforms have their own vulnerabilities. Patch them promptly.

Incident Response

Have a plan for what happens when a malicious package is detected in your registry:

  1. Quarantine the affected package version immediately. Do not delete it yet since you need it for forensics.
  2. Identify all projects that consumed the affected version.
  3. Assess what the malicious code did (data exfiltration, backdoor installation, credential theft).
  4. Remediate by upgrading affected projects to a clean version.
  5. Rotate credentials that may have been exposed.
  6. Post-mortem to understand how the malicious package was published and close the gap.

How Safeguard.sh Helps

Safeguard.sh monitors the packages your applications consume, whether from private or public registries, and provides visibility into the security posture of your dependency supply chain. It detects known vulnerabilities in your packages, tracks where they are used across your portfolio, and enforces policies that prevent vulnerable packages from reaching production. For organizations running private registries, Safeguard.sh adds the security intelligence layer that ensures your internal packages are as rigorously monitored as your external ones.

Never miss an update

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