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

Seguridad HTTPS y TLS

Implementa transporte seguro con HTTPS, configura TLS correctamente y usa HSTS para máxima protección.

HTTPS y Seguridad de Transporte

HTTPS encripta los datos en tránsito entre cliente y servidor, previniendo espionaje y manipulación. Ya no es opcional—es requerido para cualquier aplicación web moderna.

Por Qué Importa HTTPS

  • Encriptación - Los datos no pueden ser leídos por atacantes
  • Integridad - Los datos no pueden ser modificados en tránsito
  • Autenticación - Los usuarios saben que están en el sitio real
  • SEO - Google posiciona mejor sitios HTTPS
  • Características - Requerido para HTTP/2, service workers, etc.

Configurando HTTPS con Let's Encrypt

# Usando Certbot para gestión automática de certificados
sudo apt install certbot python3-certbot-nginx

# Obtener certificado
sudo certbot --nginx -d example.com -d www.example.com

# Auto-renovación se configura automáticamente
# Probar con:
sudo certbot renew --dry-run

Servidor HTTPS en Node.js

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
  key: fs.readFileSync('/path/to/privkey.pem'),
  cert: fs.readFileSync('/path/to/fullchain.pem'),
  // Configuración TLS moderna
  minVersion: 'TLSv1.2',
  ciphers: [
    'ECDHE-ECDSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-ECDSA-AES256-GCM-SHA384',
    'ECDHE-RSA-AES256-GCM-SHA384',
  ].join(':'),
};

https.createServer(options, app).listen(443);

// Redireccionar HTTP a HTTPS
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

HSTS (HTTP Strict Transport Security)

// Forzar HTTPS para todas las solicitudes futuras
app.use((req, res, next) => {
  res.setHeader(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains; preload'
  );
  next();
});

// O con Helmet
const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 31536000,        // 1 año
  includeSubDomains: true, // Aplicar a todos los subdominios
  preload: true,           // Permitir precarga HSTS
}));

Configuración HTTPS en Next.js

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=31536000; includeSubDomains; preload',
          },
        ],
      },
    ];
  },

  // Forzar HTTPS en producción
  async redirects() {
    return process.env.NODE_ENV === 'production'
      ? [
          {
            source: '/:path*',
            has: [{ type: 'header', key: 'x-forwarded-proto', value: 'http' }],
            destination: 'https://example.com/:path*',
            permanent: true,
          },
        ]
      : [];
  },
};

Certificate Pinning

// Fijación de Clave Pública (para apps móviles o aplicaciones críticas)
const https = require('https');
const crypto = require('crypto');

const expectedFingerprint = 'SHA256:XXXX...';

const options = {
  hostname: 'api.example.com',
  port: 443,
  checkServerIdentity: (host, cert) => {
    const fingerprint = crypto
      .createHash('sha256')
      .update(cert.raw)
      .digest('base64');

    if (`SHA256:${fingerprint}` !== expectedFingerprint) {
      throw new Error('¡Huella del certificado no coincide!');
    }
  },
};

https.get(options, (res) => {
  // Proceder con la solicitud
});

Configuración HTTPS en Nginx

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # Archivos de certificado
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Configuración TLS moderna
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # Configuración de sesión
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
}

Probando Configuración TLS

# Probar con SSL Labs
# Visita: https://www.ssllabs.com/ssltest/

# Probar con OpenSSL
openssl s_client -connect example.com:443 -servername example.com

# Verificar expiración del certificado
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

# Probar versión específica de TLS
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

Problemas de Contenido Mixto

<!-- MAL: Cargar recursos HTTP en página HTTPS -->
<img src="http://example.com/image.jpg">
<script src="http://cdn.example.com/script.js"></script>

<!-- BIEN: Usar HTTPS o URLs relativas al protocolo -->
<img src="https://example.com/image.jpg">
<img src="//example.com/image.jpg">

<!-- Mejor: Usar CSP para bloquear contenido mixto -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

Lista de Verificación HTTPS

  • Usa solo TLS 1.2 o 1.3 (deshabilita versiones antiguas)
  • Usa suites de cifrado fuertes
  • Habilita HSTS con max-age largo
  • Redirige todo HTTP a HTTPS
  • Usa cookies seguras (flag Secure)
  • Arregla problemas de contenido mixto
  • Configura renovación automática de certificados
  • Habilita OCSP stapling
  • Prueba con SSL Labs (apunta a calificación A+)

Continuar Aprendiendo