Qué es OWASP Top 10 y por qué todo developer debe conocerlo

D
DanisCh
(Actualizado: ) • 19 min de lectura
Qué es OWASP Top 10 y por qué todo developer debe conocerlo

Cada vez que una aplicación web es hackeada, filtrada o comprometida, suele ser por una de las mismas razones de siempre. No por exploits sofisticados de película, sino por errores comunes que llevan décadas apareciendo una y otra vez en el código de desarrolladores de todo el mundo. El OWASP Top 10 es precisamente eso: una lista de los diez riesgos de seguridad más críticos en aplicaciones web, publicada por la Open Worldwide Application Security Project, una fundación sin ánimo de lucro dedicada a mejorar la seguridad del software.

Conocer esta lista no te convierte en experto en seguridad, pero sí en un desarrollador que no comete los errores más básicos y costosos. En esta guía vas a entender cada uno de los diez riesgos, cómo funciona el ataque y cómo prevenirlo con código real.

Por qué importa el OWASP Top 10

La versión más reciente del OWASP Top 10 es la de 2021. Antes de entrar en detalle, hay tres cosas que conviene entender sobre esta lista:

No es teórica. Cada categoría está respaldada por datos reales de vulnerabilidades reportadas, CVEs documentados y análisis de miles de aplicaciones. Estos no son escenarios hipotéticos, son ataques que ocurren todos los días.

Es el estándar de la industria. PCI DSS (el estándar de seguridad de pagos con tarjeta), ISO 27001, muchos marcos de cumplimiento y la mayoría de los equipos de seguridad empresarial hacen referencia explícita al OWASP Top 10. Si trabajas en una empresa con algún nivel de compliance, ya estás obligado a conocerlo.

La mayoría de los ataques se pueden prevenir. No con herramientas caras ni con equipos de seguridad dedicados, sino escribiendo código correctamente desde el principio. La seguridad es una responsabilidad del desarrollador, no solo del equipo de operaciones o del CISO.

A01: Pérdida de control de acceso

El riesgo más crítico de la lista desde 2021. Ocurre cuando una aplicación no verifica correctamente si el usuario autenticado tiene permiso para realizar la acción que está intentando. El atacante simplemente accede a recursos o funciones que no debería poder ver.

Los escenarios más comunes son la manipulación de IDs en la URL (acceder a /facturas/1234 cuando el usuario solo debería ver /facturas/5678), escalar privilegios cambiando el rol en el token, o acceder a endpoints de administración sin autenticación.

# ❌ VULNERABLE: el servidor confía en el ID que envía el cliente
@app.route('/api/pedidos/<int:pedido_id>')
@login_required
def obtener_pedido(pedido_id):
    pedido = db.query(Pedido).filter_by(id=pedido_id).first()
    return jsonify(pedido.to_dict())   # cualquier usuario puede ver cualquier pedido


# ✅ SEGURO: verificar que el pedido pertenece al usuario autenticado
@app.route('/api/pedidos/<int:pedido_id>')
@login_required
def obtener_pedido(pedido_id):
    pedido = db.query(Pedido).filter_by(
        id=pedido_id,
        usuario_id=current_user.id   # forzar que sea del usuario en sesión
    ).first()

    if not pedido:
        abort(404)   # no revelar si existe pero pertenece a otro usuario

    return jsonify(pedido.to_dict())

Cómo prevenirlo: implementa verificación de autorización en cada endpoint, no solo autenticación. Niega el acceso por defecto y concédelo explícitamente. Usa identificadores indirectos (tokens opacos en lugar de IDs secuenciales) para que no sea trivial enumerar recursos. Registra todos los fallos de control de acceso y genera alertas cuando se detectan patrones de enumeración.

A02: Fallos criptográficos

Antes llamado "Exposición de datos sensibles". Cubre todos los errores relacionados con el uso inadecuado (o la ausencia) de criptografía para proteger datos en tránsito y en reposo. Contraseñas almacenadas en texto plano, comunicaciones sin HTTPS, claves hardcodeadas en el código fuente, uso de algoritmos obsoletos como MD5 o SHA-1 para contraseñas.

# ❌ VULNERABLE: contraseña en texto plano o con hash débil
import hashlib

def guardar_usuario(email, password):
    hash_md5 = hashlib.md5(password.encode()).hexdigest()   # MD5 es roto, no usar para contraseñas
    db.execute("INSERT INTO usuarios VALUES (?, ?)", (email, hash_md5))


# ❌ También vulnerable: SHA-256 sin salt es vulnerable a rainbow tables
def guardar_usuario_v2(email, password):
    hash_sha = hashlib.sha256(password.encode()).hexdigest()
    db.execute("INSERT INTO usuarios VALUES (?, ?)", (email, hash_sha))


# ✅ SEGURO: bcrypt con salt automático
import bcrypt

def guardar_usuario_seguro(email, password):
    # bcrypt genera un salt aleatorio e incorpora el coste computacional
    password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))
    db.execute("INSERT INTO usuarios VALUES (?, ?)", (email, password_hash))

def verificar_password(password, password_hash):
    return bcrypt.checkpw(password.encode('utf-8'), password_hash)

 

# ❌ VULNERABLE: API key en el código fuente (nunca hacer esto)
API_KEY = "sk-proj-abc123xyz789..."

# ✅ SEGURO: desde variables de entorno
import os
API_KEY = os.environ.get("API_KEY")
if not API_KEY:
    raise ValueError("API_KEY no configurada")

 

Cómo prevenirlo: usa siempre HTTPS con certificados válidos y configura HSTS. Para contraseñas, usa bcrypt, Argon2 o scrypt, nunca MD5 ni SHA sin sal. Nunca almacenes datos sensibles que no necesites. Audita el código en busca de credenciales hardcodeadas antes de cada commit (herramientas como git-secrets o truffleHog ayudan).

A03: Inyección

Las vulnerabilidades de inyección ocurren cuando datos no confiables enviados por el usuario se interpretan como comandos o código. La más conocida es la inyección SQL, pero el mismo concepto aplica a comandos del sistema operativo, expresiones LDAP, cabeceras HTTP, plantillas y más.

# ❌ VULNERABLE A SQL INJECTION: concatenación directa de input del usuario
def buscar_usuario(nombre):
    query = f"SELECT * FROM usuarios WHERE nombre = '{nombre}'"
    return db.execute(query)

# Con nombre = "' OR '1'='1" la query se convierte en:
# SELECT * FROM usuarios WHERE nombre = '' OR '1'='1'
# Lo que devuelve TODOS los usuarios de la base de datos


# ✅ SEGURO: consultas parametrizadas (el driver escapa el input automáticamente)
def buscar_usuario_seguro(nombre):
    query = "SELECT * FROM usuarios WHERE nombre = ?"
    return db.execute(query, (nombre,))   # el driver maneja el escaping


# ✅ Con SQLAlchemy ORM (aún mejor: nunca construyes SQL manualmente)
from sqlalchemy.orm import Session

def buscar_usuario_orm(session: Session, nombre: str):
    return session.query(Usuario).filter(Usuario.nombre == nombre).first()

 

# ❌ VULNERABLE A COMMAND INJECTION
import subprocess

def hacer_ping(host):
    resultado = subprocess.run(f"ping -c 1 {host}", shell=True, capture_output=True)
    return resultado.stdout

# Con host = "8.8.8.8; rm -rf /" el comando se convierte en:
# ping -c 1 8.8.8.8; rm -rf /


# ✅ SEGURO: pasar los argumentos como lista, no como string
def hacer_ping_seguro(host):
    # Validar que el host tiene el formato esperado
    import re
    if not re.match(r'^[a-zA-Z0-9.-]+$', host):
        raise ValueError("Host inválido")

    resultado = subprocess.run(["ping", "-c", "1", host], capture_output=True, timeout=5)
    return resultado.stdout

 

Cómo prevenirlo: usa siempre consultas parametrizadas o preparadas, nunca concatenes input del usuario en consultas SQL. Valida y sanitiza toda entrada antes de usarla. Aplica el principio de mínimo privilegio al usuario de base de datos de la aplicación: no necesita permisos de DROP o CREATE en producción.

A04: Diseño inseguro

Introducido en 2021, esta categoría abarca los fallos que no son errores de implementación sino de diseño: funcionalidades que nunca deberían haberse construido de esa manera. Un sistema de recuperación de contraseña que usa preguntas secretas predecibles, un proceso de pago sin límite de reintentos, o una API pública sin rate limiting son ejemplos de diseño inseguro.

# ❌ DISEÑO INSEGURO: recuperación de contraseña por "preguntas secretas"
# Las respuestas son adivinables o están en redes sociales
@app.route('/recuperar-password', methods=['POST'])
def recuperar_password():
    email = request.json['email']
    respuesta = request.json['respuesta_secreta']

    usuario = db.query(Usuario).filter_by(
        email=email,
        respuesta_secreta=respuesta   # "¿nombre de tu mascota?"
    ).first()

    if usuario:
        nueva_password = generar_password_temporal()
        usuario.password = nueva_password
        return jsonify({'nueva_password': nueva_password})   # ¡enviando password en respuesta!


# ✅ DISEÑO SEGURO: token de un solo uso enviado al email
import secrets
from datetime import datetime, timedelta

@app.route('/recuperar-password', methods=['POST'])
def recuperar_password():
    email = request.json.get('email', '').strip()
    usuario = db.query(Usuario).filter_by(email=email).first()

    # Responder siempre igual, aunque el email no exista (evita enumeración de usuarios)
    if usuario:
        token = secrets.token_urlsafe(32)
        expira = datetime.utcnow() + timedelta(hours=1)

        usuario.reset_token        = token
        usuario.reset_token_expira = expira
        db.commit()

        enviar_email(email, f"https://tuapp.com/reset?token={token}")

    return jsonify({'mensaje': 'Si el email existe, recibirás instrucciones.'})

 

Cómo prevenirlo: integra la seguridad en el diseño desde el principio, no como un añadido posterior. Modela las amenazas antes de escribir código (threat modeling). Define casos de abuso además de casos de uso. Limita los recursos y aplica rate limiting en todas las operaciones sensibles.

A05: Configuración de seguridad incorrecta

El riesgo más fácil de introducir y el más frecuente en entornos reales. Incluye: dejar credenciales por defecto, habilitar listado de directorios, exponer mensajes de error detallados en producción, permisos excesivos en recursos cloud, cabeceras de seguridad HTTP ausentes o incorrectas, y dependencias innecesarias instaladas.

# ❌ VULNERABLE: mensajes de error detallados en producción
@app.errorhandler(500)
def error_servidor(e):
    return jsonify({
        'error': str(e),             # revela detalles internos
        'traceback': traceback.format_exc()   # ¡nunca en producción!
    }), 500


# ✅ SEGURO: errores genéricos al cliente, detalle solo en logs internos
import logging

logger = logging.getLogger(__name__)

@app.errorhandler(500)
def error_servidor(e):
    logger.error(f"Error interno: {e}", exc_info=True)   # log completo solo internamente
    return jsonify({'error': 'Error interno del servidor. Por favor, inténtalo más tarde.'}), 500

 

# ✅ Cabeceras de seguridad HTTP en Flask
from flask import Flask

app = Flask(__name__)

@app.after_request
def agregar_cabeceras_seguridad(response):
    # Evita que el navegador ejecute el contenido de la respuesta en un frame de otro dominio
    response.headers['X-Frame-Options'] = 'DENY'

    # Evita que el navegador "adivine" el tipo MIME del contenido
    response.headers['X-Content-Type-Options'] = 'nosniff'

    # Política de seguridad de contenido: limita de dónde puede cargar recursos la página
    response.headers['Content-Security-Policy'] = "default-src 'self'"

    # Fuerza HTTPS durante 1 año
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'

    # Controla qué información de referencia se envía
    response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'

    # Eliminar cabeceras que revelan la tecnología usada
    response.headers.pop('X-Powered-By', None)
    response.headers.pop('Server', None)

    return response

 

Cómo prevenirlo: usa entornos separados para desarrollo, staging y producción con configuraciones distintas. Automatiza la configuración con IaC (Infrastructure as Code) para que sea reproducible y auditada. Revisa periódicamente los permisos en cloud (IAM mínimo privilegio). Usa herramientas como securityheaders.com para auditar las cabeceras HTTP de tu aplicación.

A06: Componentes vulnerables y desactualizados

Las librerías, frameworks y dependencias de terceros que usa tu aplicación pueden tener vulnerabilidades conocidas. Si no las actualizas, estás exponiendo tu aplicación a ataques para los que ya existen exploits públicos. El ataque masivo a Log4Shell en 2021 es el ejemplo más impactante reciente: millones de aplicaciones vulnerables por una librería de logging de Java que nadie había pensado en actualizar.

# Verificar vulnerabilidades conocidas en dependencias Python
pip install safety
safety check

# O con pip-audit (herramienta oficial de PyPA)
pip install pip-audit
pip-audit

# Salida de ejemplo:
# Found 2 known vulnerabilities in 1 package
# Package    Version  ID                  Fix Versions
# requests   2.25.0   PYSEC-2023-74       2.31.0

 

# Para proyectos Node.js
npm audit
npm audit fix

# Ver solo vulnerabilidades críticas y altas
npm audit --audit-level=high
# Mantener dependencias actualizadas automáticamente
# Configura Dependabot en GitHub (.github/dependabot.yml)
# Este archivo hace que GitHub cree PRs automáticos cuando hay actualizaciones

version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"

Cómo prevenirlo: inventaría todas tus dependencias y sus versiones (Software Bill of Materials). Suscríbete a los avisos de seguridad de los proyectos que usas. Configura Dependabot o Renovate para automatizar las actualizaciones. Elimina dependencias que no uses.

A07: Fallos de identificación y autenticación

Autenticación rota: contraseñas débiles permitidas, sin protección contra fuerza bruta, gestión insegura de sesiones, tokens predecibles. Un sistema de autenticación mal implementado es la puerta de entrada más directa a toda la aplicación.

# ❌ VULNERABLE: sin protección contra fuerza bruta
@app.route('/login', methods=['POST'])
def login():
    email    = request.json['email']
    password = request.json['password']

    usuario = db.query(Usuario).filter_by(email=email).first()
    if usuario and bcrypt.checkpw(password.encode(), usuario.password_hash):
        session['usuario_id'] = usuario.id
        return jsonify({'ok': True})

    return jsonify({'error': 'Credenciales incorrectas'}), 401
    # Un atacante puede hacer millones de intentos sin restricción


# ✅ SEGURO: rate limiting + bloqueo por intentos fallidos
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(app, key_func=get_remote_address)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")   # máximo 5 intentos por minuto por IP
def login():
    email    = request.json.get('email', '').strip().lower()
    password = request.json.get('password', '')

    # Verificar si la cuenta está bloqueada por intentos fallidos
    intentos = cache.get(f"intentos_login:{email}", 0)
    if intentos >= 10:
        return jsonify({'error': 'Cuenta bloqueada temporalmente. Inténtalo en 15 minutos.'}), 429

    usuario = db.query(Usuario).filter_by(email=email).first()

    # Comparar siempre el hash aunque el usuario no exista (evita timing attacks)
    hash_dummy = b'$2b$12$' + b'x' * 53
    hash_real  = usuario.password_hash if usuario else hash_dummy
    es_valido  = bcrypt.checkpw(password.encode(), hash_real) and usuario is not None

    if es_valido:
        cache.delete(f"intentos_login:{email}")
        session['usuario_id'] = usuario.id
        session.permanent = True
        return jsonify({'ok': True})

    # Incrementar contador de intentos fallidos con expiración de 15 minutos
    cache.set(f"intentos_login:{email}", intentos + 1, timeout=900)
    return jsonify({'error': 'Credenciales incorrectas'}), 401

 

Cómo prevenirlo: implementa autenticación multifactor (MFA). Rechaza contraseñas comunes con listas de contraseñas filtradas (HIBP). Usa tokens de sesión largos y aleatorios, nunca predecibles. Invalida los tokens de sesión al cerrar sesión. Añade rate limiting en todos los endpoints de autenticación.

A08: Fallos en la integridad del software y los datos

Cubre dos escenarios: deserialización insegura de datos y ataques a la cadena de suministro de software. En el primer caso, el atacante manipula objetos serializados para ejecutar código arbitrario. En el segundo, compromete una dependencia o el pipeline de CI/CD para inyectar código malicioso.

# ❌ VULNERABLE: deserializar datos no confiables con pickle
import pickle
import base64

@app.route('/cargar-sesion', methods=['POST'])
def cargar_sesion():
    datos_b64 = request.json['datos']
    datos = pickle.loads(base64.b64decode(datos_b64))   # NUNCA deserializar pickle de usuarios
    return jsonify(datos)

# Un atacante puede enviar un objeto pickle malicioso que ejecute código al deserializarse:
# import os; os.system('rm -rf /')


# ✅ SEGURO: usar JSON para datos del cliente, nunca pickle
import json
import hmac
import hashlib

SECRET = os.environ.get('SESSION_SECRET')

def crear_token_sesion(datos):
    payload   = json.dumps(datos)
    firma     = hmac.new(SECRET.encode(), payload.encode(), hashlib.sha256).hexdigest()
    return base64.b64encode(f"{payload}.{firma}".encode()).decode()

def verificar_token_sesion(token):
    decoded   = base64.b64decode(token.encode()).decode()
    payload, firma_recibida = decoded.rsplit('.', 1)
    firma_esperada = hmac.new(SECRET.encode(), payload.encode(), hashlib.sha256).hexdigest()

    if not hmac.compare_digest(firma_recibida, firma_esperada):
        raise ValueError("Token inválido o manipulado")

    return json.loads(payload)

 

Cómo prevenirlo: nunca deserialices datos no confiables con pickle, Marshal o formatos similares. Usa JSON. Verifica la integridad de los paquetes que descargas (checksums, firmas GPG). Configura tu pipeline de CI/CD para que solo pueda desplegarse código revisado y firmado.

A09: Fallos en el registro y monitorización de seguridad

Si no registras lo que ocurre en tu aplicación, no puedes detectar ataques en curso, no puedes investigar incidentes después de que ocurran y no puedes cumplir con regulaciones como GDPR o PCI DSS. Este riesgo no genera vulnerabilidades directas, pero convierte cualquier otro ataque en mucho más difícil de detectar y contener.

# ✅ Logging de seguridad estructurado
import logging
import json
from datetime import datetime

class SecurityLogger:
    def __init__(self):
        self.logger = logging.getLogger('security')
        handler = logging.FileHandler('/var/log/app/security.log')
        handler.setFormatter(logging.Formatter('%(message)s'))
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.INFO)

    def _log(self, nivel, evento, datos):
        entrada = {
            'timestamp':  datetime.utcnow().isoformat(),
            'nivel':      nivel,
            'evento':     evento,
            **datos
        }
        getattr(self.logger, nivel)(json.dumps(entrada))

    def login_exitoso(self, usuario_id, ip):
        self._log('info', 'LOGIN_EXITOSO', {'usuario_id': usuario_id, 'ip': ip})

    def login_fallido(self, email, ip, motivo):
        self._log('warning', 'LOGIN_FALLIDO', {'email': email, 'ip': ip, 'motivo': motivo})

    def acceso_denegado(self, usuario_id, recurso, ip):
        self._log('warning', 'ACCESO_DENEGADO', {
            'usuario_id': usuario_id, 'recurso': recurso, 'ip': ip
        })

    def dato_sensible_accedido(self, usuario_id, tipo_dato, recurso_id):
        self._log('info', 'DATO_SENSIBLE_ACCEDIDO', {
            'usuario_id': usuario_id, 'tipo_dato': tipo_dato, 'recurso_id': recurso_id
        })

    def accion_admin(self, admin_id, accion, objetivo):
        self._log('info', 'ACCION_ADMIN', {
            'admin_id': admin_id, 'accion': accion, 'objetivo': objetivo
        })


security_log = SecurityLogger()

# Uso en el código
@app.route('/login', methods=['POST'])
def login():
    ip = request.remote_addr
    # ...
    if es_valido:
        security_log.login_exitoso(usuario.id, ip)
    else:
        security_log.login_fallido(email, ip, 'credenciales_incorrectas')

 

Qué debes registrar siempre: intentos de login (exitosos y fallidos), cambios de contraseña, accesos a datos sensibles, acciones administrativas, errores de autorización, cambios en configuración de seguridad. Qué nunca debes registrar: contraseñas, tokens completos, datos de tarjetas de crédito, información personal innecesaria.

A10: Falsificación de peticiones del lado del servidor (SSRF)

Server-Side Request Forgery: el atacante consigue que el servidor haga peticiones HTTP a destinos arbitrarios, incluyendo servicios internos que no son accesibles desde Internet. Es especialmente peligroso en entornos cloud donde los metadatos de la instancia (credenciales temporales de AWS, GCP o Azure) son accesibles por HTTP desde el propio servidor.

# ❌ VULNERABLE A SSRF: el servidor hace una petición a la URL que indique el usuario
import requests

@app.route('/preview', methods=['POST'])
def preview_url():
    url = request.json['url']
    respuesta = requests.get(url)   # ¡el atacante controla el destino!
    return jsonify({'contenido': respuesta.text})

# El atacante puede enviar:
# url = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# Esto hace que el servidor pida las credenciales IAM de la instancia EC2 al servicio de metadatos
# y devuelva las credenciales al atacante


# ✅ SEGURO: validar y restringir las URLs permitidas
import ipaddress
from urllib.parse import urlparse

DOMINIOS_PERMITIDOS = {'ejemplo.com', 'cdn.ejemplo.com'}

def es_url_segura(url):
    try:
        parsed = urlparse(url)

        # Solo permitir HTTPS
        if parsed.scheme != 'https':
            return False

        # Solo dominios de la lista blanca
        if parsed.hostname not in DOMINIOS_PERMITIDOS:
            return False

        # Bloquear IPs privadas, loopback y metadatos cloud
        try:
            ip = ipaddress.ip_address(parsed.hostname)
            if ip.is_private or ip.is_loopback or ip.is_link_local:
                return False
        except ValueError:
            pass   # no es una IP, es un hostname, continuar

        return True

    except Exception:
        return False


@app.route('/preview', methods=['POST'])
def preview_url():
    url = request.json.get('url', '')

    if not es_url_segura(url):
        return jsonify({'error': 'URL no permitida'}), 400

    respuesta = requests.get(url, timeout=5, allow_redirects=False)
    return jsonify({'titulo': extraer_titulo(respuesta.text)})

 

Cómo prevenirlo: valida y sanitiza todas las URLs antes de hacer peticiones. Usa listas blancas de dominios permitidos, nunca listas negras. Bloquea peticiones a rangos de IP privados y de metadatos cloud. Deshabilita las redirecciones HTTP automáticas. En AWS, activa IMDSv2 que requiere autenticación para acceder al servicio de metadatos.

Cómo integrar OWASP en tu flujo de desarrollo

Conocer la lista es el primer paso. Aplicarla requiere incorporarla al proceso de desarrollo, no tratarla como una auditoría puntual.

Durante el diseño: realiza un análisis de amenazas (threat modeling) antes de escribir código. Pregúntate para cada funcionalidad: ¿qué pasaría si un atacante envía datos inesperados aquí? ¿Qué ocurre si este endpoint lo llama alguien no autorizado?

Durante el desarrollo: añade análisis estático de seguridad (SAST) a tu pipeline de CI/CD. Herramientas como Bandit para Python, Semgrep para múltiples lenguajes o SonarQube detectan patrones vulnerables automáticamente.

# Instalar y ejecutar Bandit para análisis de seguridad en Python
pip install bandit
bandit -r ./src -ll   # -ll muestra solo severidad media y alta

# Semgrep: reglas de seguridad para múltiples lenguajes
# (incluye reglas específicas del OWASP Top 10)
semgrep --config=p/owasp-top-ten .

 

Durante la revisión de código: crea un checklist de seguridad basado en el OWASP Top 10 para las code reviews. Cada PR que toque autenticación, autorización, manejo de datos de usuario o llamadas externas debe pasar por ese checklist.

En producción: monitoriza los logs de seguridad, configura alertas para patrones anómalos (muchos fallos de login, accesos a recursos inexistentes en secuencia, peticiones inusuales desde una misma IP) y ten un plan de respuesta a incidentes documentado antes de que ocurra el primero.

Resumen

CategoríaEn una frasePrevención clave
A01: Control de accesoEl usuario accede a recursos de otrosVerificar autorización en cada petición
A02: Fallos criptográficosDatos sensibles sin cifrar o mal cifradosbcrypt para contraseñas, HTTPS siempre, sin credenciales en código
A03: InyecciónInput del usuario ejecutado como códigoConsultas parametrizadas, nunca concatenar SQL
A04: Diseño inseguroLa arquitectura tiene fallos de seguridad de raízThreat modeling, rate limiting, tokens seguros
A05: Mala configuraciónErrores en la configuración del entornoCabeceras HTTP, sin errores detallados en producción
A06: Componentes vulnerablesDependencias con vulnerabilidades conocidasActualizar dependencias, Dependabot, pip-audit
A07: Autenticación rotaLogin sin protección ni MFARate limiting, MFA, tokens seguros
A08: Integridad del softwareDeserialización insegura, cadena de suministroNunca pickle de usuarios, verificar checksums
A09: Falta de monitorizaciónLos ataques pasan sin detectarseLogging estructurado de eventos de seguridad
A10: SSRFEl servidor hace peticiones a destinos del atacanteLista blanca de dominios, bloquear IPs privadas

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a