Esta es la Parte 2 de una serie de 2 artículos sobre cómo proteger sitios Astro de ataques a la cadena de suministro de npm. La Parte 1 cubrió la comprensión de la amenaza y la respuesta inmediata. Este artículo se centra en medidas de seguridad a largo plazo, monitorización y buenas prácticas.
SponsoredKits de inicio de alta calidad con flujo de autenticación integrada (Auth.js), carga de objetos (AWS, Clouflare R2, Firebase Storage, Supabase Storage), pagos integrados (Stripe, LemonSqueezy), flujo de verificación de correo electrónico (Resend, Postmark, Sendgrid) y mucho más . Compatible con cualquier base de datos (Redis, Postgres, MongoDB, SQLite, Firestore).
Get all 3 kits Bundle ↗
Next.js Starter Kit SvelteKit Starter Kit Astro Starter KitOne-time license · Lifetime updates
En la Parte 1, cubrimos la respuesta inmediata al ataque Shai-Hulud 2.0. Ahora es el momento de implementar medidas de seguridad integrales que protejan su proyecto Astro de futuros ataques a la cadena de suministro. No son parches temporales: son prácticas esenciales que deberían formar parte de su flujo de trabajo de desarrollo.
Requisitos previos
Necesitará lo siguiente:
- Haber completado la Parte 1 de esta serie
1. Implementar bloqueo y verificación de paquetes
La primera línea de defensa contra ataques a la cadena de suministro es asegurarse de que siempre instala exactamente las versiones de los paquetes que ha probado y verificado.
1.1 Usar números de versión exactos
Nunca use rangos de versión en su package.json para dependencias de producción:
// ❌ Malo: permite actualizaciones automáticas{ "dependencies": { "astro": "^4.16.1", // Puede instalar 4.16.x o 4.x.x "sharp": "~0.33.1" // Puede instalar 0.33.x }}
// ✅ Bueno: solo versiones exactas{ "dependencies": { "astro": "4.16.1", "sharp": "0.33.1" }}1.2 Configurar npm/pnpm para seguridad
Cree un archivo .npmrc en la raíz de su proyecto con configuraciones de seguridad estrictas:
# Always use exact versions when saving packagessave-exact=true
# Ensure package-lock.json is always created and usedpackage-lock=true
# Run audit on every installaudit=true
# Set minimum audit level (low, moderate, high, critical)audit-level=moderate
# Fail install if vulnerabilities are foundaudit-report=truePara usuarios de pnpm (recomendado), configure funciones de seguridad adicionales:
# File: .npmrc (for pnpm)
# Require exact versionssave-exact=true
# Strict peer dependenciesstrict-peer-dependencies=true
# Verify package integrity using checksumspackage-import-method=copy
# Run security audit on installaudit=true
# Only allow packages from verified registriesregistry=https://registry.npmjs.org/
# Verify SSL certificatesstrict-ssl=true1.3 Confirmar siempre los archivos de bloqueo
Su archivo de bloqueo (package-lock.json o pnpm-lock.yaml) es fundamental para builds reproducibles:
# Ensure lock file is tracked in gitgit add pnpm-lock.yamlgit commit -m "chore: commit lock file for security"Actualice su .gitignore para asegurarse de que los archivos de bloqueo nunca se ignoren:
# Make sure these are NOT in .gitignore:# pnpm-lock.yaml# package-lock.json# yarn.lock
# But DO ignorenode_modules/2. Usar herramientas de escaneo de dependencias
Las herramientas de escaneo automatizado pueden detectar vulnerabilidades antes de que lleguen a producción. Configuremos un flujo de trabajo de escaneo integral.
2.1 Escaneo de seguridad con Snyk y GitHub Actions
Snyk es una plataforma de seguridad para desarrolladores líder, especializada en escanear sus dependencias en busca de vulnerabilidades.
Cree un flujo de trabajo de GitHub Actions para el escaneo con Snyk:
name: Snyk Security Scan
on: push: branches: [main, develop] pull_request: branches: [main, develop] schedule: - cron: '0 2 * * *' workflow_dispatch:
jobs: snyk-scan: runs-on: ubuntu-latest
permissions: contents: read
steps: - name: Checkout code uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'pnpm'
- name: Install pnpm uses: pnpm/action-setup@v2 with: version: 8
- name: Install dependencies (safely) run: pnpm install --frozen-lockfile --ignore-scripts
- name: Run npm audit run: pnpm audit --audit-level=moderate continue-on-error: true id: npm-audit
- name: Run Snyk security scan uses: snyk/actions/node@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --severity-threshold=high --all-projects continue-on-error: true id: snyk-scan
- name: Generate Security Report if: always() run: | echo "# Snyk Security Scan Report" > security-report.md echo "Generated: $(date)" >> security-report.md echo "" >> security-report.md echo "## Scan Results" >> security-report.md echo "- npm audit: ${{ steps.npm-audit.outcome }}" >> security-report.md echo "- Snyk scan: ${{ steps.snyk-scan.outcome }}" >> security-report.md
- name: Upload Security Report uses: actions/upload-artifact@v4 if: always() with: name: snyk-security-report-${{ github.run_number }} path: security-report.md retention-days: 90Snyk ofrece monitorización continua de sus dependencias, rastreando nuevas vulnerabilidades incluso después de que su código se haya fusionado. Proporciona sugerencias de corrección que identifican exactamente qué actualizaciones resolverán problemas conocidos. Con una sólida integración en IDE y CI/CD, Snyk le ayuda a detectar problemas de seguridad antes de que sus dependencias lleguen a producción. Además, Snyk ofrece un generoso plan gratuito para repositorios públicos y de código abierto, lo que lo hace accesible para la comunidad de desarrolladores en general.
2.2 Escáner de vulnerabilidades Trivy con GitHub Actions
Trivy es un escáner de vulnerabilidades de código abierto que ofrece un escaneo más amplio, incluyendo seguridad de infraestructura y contenedores.
Configure Trivy con GitHub Actions:
name: Trivy Scan
on: push: branches: [main, develop] pull_request: branches: [main, develop] schedule: - cron: '0 4 * * *' workflow_dispatch:
jobs: trivy-scan: runs-on: ubuntu-latest
permissions: contents: read security-events: write
steps: - name: Checkout code uses: actions/checkout@v4
- name: Install pnpm uses: pnpm/action-setup@v2 with: version: 8
- name: Install dependencies (safely) run: pnpm install --frozen-lockfile --ignore-scripts
- name: Trivy vulnerability scan (code & dependencies) uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'sarif' output: 'trivy-results.sarif' severity: 'HIGH,CRITICAL'
- name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results.sarif'Trivy es un escáner de seguridad de código abierto que ofrece una cobertura amplia de vulnerabilidades en todo su proyecto: no solo en node_modules, sino también en contenedores, paquetes del sistema operativo, archivos de configuración e infraestructura como código. Sus resultados pueden exportarse en formato SARIF, lo que permite una integración fluida con las funciones de seguridad de GitHub. Trivy es gratuito, sencillo de automatizar y está bien adaptado tanto para flujos de trabajo locales como de CI/CD.
2.3 Configurar escaneo de seguridad local
No espere a que CI/CD detecte los problemas. Escanee localmente antes de hacer push:
# Install Snyk CLI globallynpm install -g snyk
# Authenticate with Snyksnyk auth
# Scan your projectsnyk test
# Monitor your project (sends snapshot to Snyk for continuous monitoring)snyk monitorAñada un hook de git pre-push:
# Install husky if you haven't alreadypnpm add -D husky
# Initialize huskynpx husky init
# Create pre-push hookcat > .husky/pre-push << 'EOF'#!/bin/sh. "$(dirname "$0")/_/husky.sh"
echo "🔍 Running security scan before push..."pnpm audit --audit-level=moderateEOF
chmod +x .husky/pre-push3. Asegurar su pipeline de CI/CD
El ataque Shai-Hulud 2.0 apuntó específicamente a entornos de CI/CD donde los secretos están fácilmente disponibles. Así es como puede reforzar su pipeline.
3.1 Usar permisos mínimos
Configure sus GitHub Actions con el principio de mínimo privilegio:
name: Deploy to Production
on: push: branches: [main]
# Set minimal default permissionspermissions: contents: read
jobs: deploy: runs-on: ubuntu-latest
# Override only for specific needs permissions: contents: read deployments: write id-token: write # For OIDC authentication
steps: - uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'pnpm'
- name: Install pnpm uses: pnpm/action-setup@v2 with: version: 8
# Critical: Disable install scripts in CI - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts
- name: Build env: # Use environment-specific secrets DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }} API_KEY: ${{ secrets.PROD_API_KEY }} run: pnpm run build
- name: Deploy run: pnpm run deploy3.2 Desactivar scripts de instalación
Los scripts de instalación son el vector de ataque principal. Desactívelos siempre en CI/CD:
# Always use --ignore-scripts in CI- name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scriptsPara paquetes que legítimamente necesitan scripts de instalación (como sharp o esbuild), reconstrúyalos explícitamente:
import { execSync } from 'child_process'
console.log('🔒 Running secure installation...\n')
try { // Install without running any scripts console.log('📦 Installing packages without scripts...') execSync('pnpm install --frozen-lockfile --ignore-scripts', { stdio: 'inherit', })
console.log('\n✅ Dependencies installed safely')
// Explicitly rebuild trusted packages that need native bindings const TRUSTED_PACKAGES = [ 'sharp', // Image processing 'esbuild', // Bundler '@parcel/watcher', // File watcher // Add other trusted packages here ]
console.log('\n📦 Rebuilding trusted native packages...') for (const pkg of TRUSTED_PACKAGES) { try { console.log(` - Rebuilding ${pkg}...`) execSync(`pnpm rebuild ${pkg}`, { stdio: 'inherit' }) console.log(` ✅ ${pkg} rebuilt successfully`) } catch (error) { console.error(` ❌ Failed to rebuild ${pkg}`) // Don't fail the whole process - package might not be installed } }
console.log('\n✅ Secure installation complete!')} catch (error) { console.error('\n❌ Installation failed:', error.message) process.exit(1)}Actualice su package.json:
{ "scripts": { "install:safe": "tsx scripts/secure-install.ts", "build": "astro build", "dev": "astro dev" }}3.3 Usar aislamiento de entornos
Use secretos y credenciales diferentes para cada entorno (dev, staging, producción) para contener posibles brechas y evitar que las filtraciones se propaguen entre entornos.
name: Deploy to Staging
on: push: branches: [develop]
jobs: deploy-staging: runs-on: ubuntu-latest environment: staging # GitHub environment
steps: - uses: actions/checkout@v4
- name: Build and Deploy env: # Staging-specific secrets DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }} API_KEY: ${{ secrets.STAGING_API_KEY }} run: | pnpm install --frozen-lockfile --ignore-scripts pnpm run build pnpm run deploy:stagingname: Deploy to Production
on: push: branches: [main]
jobs: deploy-production: runs-on: ubuntu-latest environment: production # Different environment
steps: - uses: actions/checkout@v4
- name: Build and Deploy env: # Production-specific secrets DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }} API_KEY: ${{ secrets.PROD_API_KEY }} run: | pnpm install --frozen-lockfile --ignore-scripts pnpm run build pnpm run deploy:production3.4 Usar OIDC en lugar de tokens de larga duración
Para despliegues en la nube, use OpenID Connect (OIDC) en lugar de almacenar credenciales de larga duración:
name: Deploy to AWS
on: push: branches: [main]
permissions: id-token: write # Required for OIDC contents: read
jobs: deploy: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v4
# Use OIDC to get temporary credentials - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole aws-region: us-east-1
# Now you have temporary AWS credentials - name: Deploy to S3 run: | pnpm run build aws s3 sync dist/ s3://my-bucket/Al adoptar OIDC, elimina la necesidad de almacenar credenciales de nube de larga duración en GitHub, reduciendo significativamente el riesgo si su entorno de CI alguna vez se ve comprometido. En su lugar, GitHub Actions puede solicitar credenciales temporales que expiran automáticamente para cada ejecución del flujo de trabajo. Cada evento de autenticación se registra y es auditable en AWS CloudTrail, garantizando trazabilidad completa y transparencia de seguridad.
4. Configurar la automatización de npm audit
Convierta las auditorías de seguridad en una parte habitual de su flujo de trabajo de desarrollo.
4.1 Configurar hooks pre-commit
Configurar hooks pre-commit añade un punto de control automatizado en su flujo de trabajo para evitar que confirme código inseguro. Cada vez que intente hacer un commit, Husky activará la auditoría de seguridad (usando pnpm audit --audit-level=moderate). Si se detectan vulnerabilidades por encima del umbral, el commit se bloquea hasta que se resuelvan los problemas o se omitan intencionalmente. Esto ayuda a garantizar que solo se confirme código seguro en el repositorio, manteniendo la integridad de su proyecto desde el principio.
#!/bin/sh. "$(dirname "$0")/_/husky.sh"
echo "🔍 Running pre-commit security checks...\n"
# Run auditecho "📦 Checking for vulnerabilities..."pnpm audit --audit-level=moderate
if [ $? -ne 0 ]; then echo "\n❌ Security vulnerabilities found!" echo "Run 'pnpm audit fix' to resolve issues." echo "Or use --no-verify to skip (not recommended)." exit 1fi
echo "✅ Security checks passed!\n"Además, actualice los scripts en package.json para ejecutar las auditorías a través del gestor de paquetes correspondiente.
{ "scripts": { "audit": "pnpm audit --audit-level=moderate", "audit:fix": "pnpm audit fix", "audit:production": "pnpm audit --production --audit-level=high", "precommit": "pnpm run audit", "prepush": "pnpm run audit:production" }}4.2 Crear informes de seguridad automatizados
Esta sección muestra cómo crear informes de seguridad automatizados para su proyecto.
import { execSync } from 'child_process'import { writeFileSync } from 'fs'
const generateSecurityReport = () => { console.log('📊 Generating security report...\n')
const report = { timestamp: new Date().toISOString(), auditResults: null as any, outdatedPackages: null as any, recommendations: [] as string[], }
try { // Run npm audit const auditOutput = execSync('pnpm audit --json', { encoding: 'utf-8', }) report.auditResults = JSON.parse(auditOutput) } catch (error: any) { // Audit exits with non-zero if vulnerabilities found if (error.stdout) { report.auditResults = JSON.parse(error.stdout) } }
// Generate recommendations const vulnerabilities = report.auditResults?.metadata?.vulnerabilities if (vulnerabilities) { const { critical, high, moderate } = vulnerabilities
if (critical > 0) { report.recommendations.push( `🚨 CRITICAL: ${critical} critical vulnerabilities require immediate attention` ) } if (high > 0) { report.recommendations.push( `⚠️ HIGH: ${high} high-severity vulnerabilities should be addressed soon` ) } if (moderate > 0) { report.recommendations.push( `ℹ️ MODERATE: ${moderate} moderate vulnerabilities should be reviewed` ) } }
// Write report const reportPath = `security-report-${Date.now()}.json` writeFileSync(reportPath, JSON.stringify(report, null, 2))
console.log(`✅ Security report generated: ${reportPath}\n`)
// Print summary console.log('📊 Summary:') report.recommendations.forEach(rec => console.log(rec))
return report}
generateSecurityReport()- Ejecute un comando
pnpm auditpara escanear sus dependencias en busca de vulnerabilidades y capturar los resultados en JSON. - Recopile la marca de tiempo del descubrimiento, los resultados de la auditoría y las recomendaciones de remediación.
- Resuma las vulnerabilidades por gravedad (critical, high, moderate) y proporcione recomendaciones accionables si hay problemas.
- Escriba el informe de seguridad resultante en un archivo que pueda revisar o compartir con su equipo.
Al integrar este script en su flujo de trabajo, garantiza una visibilidad clara y regular del estado de seguridad de su proyecto. Esto facilita el seguimiento y la corrección efectiva de vulnerabilidades, y respalda una política de monitorización continua de la seguridad.
Conclusión
Los pasos que hemos cubierto aquí establecen una defensa fiable: use siempre versiones exactas y archivos de bloqueo, automatice las comprobaciones de seguridad en su pipeline de CI/CD, desactive los scripts de instalación durante los builds de producción, use OIDC en lugar de credenciales estáticas y convierta las revisiones de seguridad en una parte estándar de su proceso con git.
Si tiene preguntas o comentarios, no dude en contactarme en Twitter.