Description
Downloading dependencies over unencrypted protocols such as HTTP or FTP exposes them to interception and modification in transit. An attacker on the network, a malicious proxy, DNS spoofing, or a compromised mirror may replace an artifact such as a tarball, causing malicious code to run during installation or the build.
Potential impact
- Arbitrary code execution through modified packages or installation scripts
- Supply-chain compromise when a malicious dependency is included in a product and reaches production
- Loss of build integrity and trust in released artifacts
- Theft or modification of metadata and artifacts exposed in transit
Remediation
- Protect transport: use HTTPS for npm tarball URLs, and supported HTTPS or verified SSH connections for Git dependencies.
- Reduce direct URLs: prefer version ranges from an official registry such as npm where possible.
- Enforce TLS validation: use
strict-ssl=trueand an HTTPS registry in.npmrc, with valid certificates and appropriate TLS settings on internal artifact servers. - Verify integrity: commit a reviewed
package-lock.jsonornpm-shrinkwrap.jsonand usenpm ci. The lockfile'sintegritycheck verifies that bytes match the recorded artifact; it does not establish that the artifact itself is trustworthy. - Check supplied checksums or signatures, such as GPG signatures, against trusted values or keys.
- Restrict CI network access: block outbound HTTP and allow communication only with approved domains.
- Review installation scripts: consider
npm config set ignore-scripts truein CI and allow only the scripts you need separately.
Examples
Before
HTTP illustrates an unencrypted download. Current npm does not support the FTP URL, so installation may fail; do not assume that an FTP transfer actually occurs.
javascript
{
"name": "demo-app",
"version": "1.0.0",
"dependencies": {
"left-pad": "http://mirror.local/libs/left-pad-1.3.0.tgz",
"my-utils": "ftp://downloads.example.org/my-utils-2.1.0.tgz"
}
}
After
package.json:
json
{
"name": "demo-app",
"version": "1.0.0",
"dependencies": {
"left-pad": "^1.3.0",
"my-utils": "https://repo.example.com/npm/my-utils-2.1.0.tgz"
}
}
Project or CI .npmrc:
ini
registry=https://registry.npmjs.org/
strict-ssl=true
# Use HTTPS for the internal registry too
@company:registry=https://repo.example.com/npm/
Explanation:
- Before: An HTTP download risks tarball tampering in transit. The FTP entry is unsupported by current npm. Malicious package code or scripts such as
postinstallmay execute in the build or developer environment. - After: HTTPS encrypts and authenticates transport. Using a registry version range reduces direct URL dependencies, and
.npmrcenforces TLS validation withstrict-ssl. Downloads that differ from the reviewed lockfile'sintegrityvalue are rejected. This does not make a malicious package or a jointly tampered lockfile trustworthy.