TechLead
Lección 9 de 9
5 min de lectura
Seguridad Web

Pruebas y Monitoreo de Seguridad

Implementa pruebas de seguridad en tu pipeline CI/CD y configura monitoreo para eventos de seguridad.

Pruebas de Seguridad

Las pruebas de seguridad deben ser automatizadas e integradas en tu flujo de trabajo de desarrollo. Encuentra vulnerabilidades antes que los atacantes.

Pruebas de Seguridad de Aplicaciones Estáticas (SAST)

# Plugin de seguridad de ESLint
npm install --save-dev eslint-plugin-security

# .eslintrc.js
module.exports = {
  plugins: ['security'],
  extends: ['plugin:security/recommended'],
  rules: {
    'security/detect-object-injection': 'error',
    'security/detect-non-literal-regexp': 'error',
    'security/detect-unsafe-regex': 'error',
    'security/detect-buffer-noassert': 'error',
    'security/detect-child-process': 'warn',
    'security/detect-disable-mustache-escape': 'error',
    'security/detect-eval-with-expression': 'error',
    'security/detect-no-csrf-before-method-override': 'error',
    'security/detect-possible-timing-attacks': 'error',
  },
};

Semgrep

# Instalar Semgrep
pip install semgrep

# Ejecutar reglas de seguridad
semgrep --config auto .

# Ejecutar conjuntos de reglas específicos
semgrep --config p/security-audit .
semgrep --config p/javascript .

# Reglas personalizadas
# semgrep.yml
rules:
  - id: hardcoded-password
    pattern: password = "..."
    message: "Contraseña hardcodeada detectada"
    severity: ERROR
    languages: [javascript, typescript]

Pruebas de Seguridad de Aplicaciones Dinámicas (DAST)

# OWASP ZAP Docker
docker run -t owasp/zap2docker-stable zap-baseline.py -t https://tu-app.com

# Generar reporte HTML
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable \
  zap-baseline.py -t https://tu-app.com -r report.html

Escaneo de Dependencias

# Workflow de GitHub Actions
name: Escaneo de Seguridad

on: [push, pull_request]

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

      - name: Ejecutar npm audit
        run: npm audit --audit-level=high

      - name: Ejecutar Snyk para verificar vulnerabilidades
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

      - name: Ejecutar escáner de vulnerabilidades Trivy
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          severity: 'HIGH,CRITICAL'

Pruebas de Cabeceras de Seguridad

// Probar cabeceras de seguridad con Playwright
import { test, expect } from '@playwright/test';

test('las cabeceras de seguridad están configuradas correctamente', async ({ page }) => {
  const response = await page.goto('https://tu-app.com');
  const headers = response.headers();

  expect(headers['strict-transport-security']).toContain('max-age=');
  expect(headers['x-frame-options']).toBe('DENY');
  expect(headers['x-content-type-options']).toBe('nosniff');
  expect(headers['content-security-policy']).toBeDefined();
  expect(headers['referrer-policy']).toBeDefined();
});

test('sin datos sensibles en respuesta', async ({ page }) => {
  const response = await page.goto('https://tu-app.com/api/user');
  const body = await response.json();

  // Asegurar que password nunca se retorna
  expect(body.password).toBeUndefined();
  expect(body.passwordHash).toBeUndefined();
});

Automatización de Pruebas de Penetración

// Pruebas de seguridad personalizadas
describe('Pruebas de Seguridad', () => {
  test('protección contra inyección SQL', async () => {
    const maliciousInputs = [
      "'; DROP TABLE users; --",
      "' OR '1'='1",
      "1; SELECT * FROM users",
    ];

    for (const input of maliciousInputs) {
      const response = await fetch('/api/search', {
        method: 'POST',
        body: JSON.stringify({ query: input }),
      });

      // No debe retornar error indicando ejecución SQL
      expect(response.status).not.toBe(500);
      const data = await response.json();
      expect(data.error).not.toContain('SQL');
    }
  });

  test('protección XSS', async () => {
    const xssPayloads = [
      '<script>alert(1)</script>',
      '<img src=x onerror=alert(1)>',
      'javascript:alert(1)',
    ];

    for (const payload of xssPayloads) {
      const response = await fetch('/api/comment', {
        method: 'POST',
        body: JSON.stringify({ text: payload }),
      });

      const data = await response.json();
      // Payload debe ser sanitizado o rechazado
      expect(data.text).not.toContain('