Categoría: Bases de Datos

  • MongoDB — El Ministerio de las Promesas Imposibles

    MongoDB — El Ministerio de las Promesas Imposibles

    1. Presentación de la práctica

    Bienvenidos al Ministerio de las Promesas Imposibles, una institución ficticia donde los políticos registran sus promesas electorales y el presupuesto necesario para cumplirlas.

    El problema es que el Ministerio ha perdido el control de sus datos. Hay promesas que se repiten, presupuestos disparatados, proyectos que nadie recuerda haber aprobado y políticos que llevan años prometiendo lo mismo.

    El Gobierno ha decidido contratar a un grupo de administradores de sistemas para modernizar la base de datos.

    Vuestra misión será construir y gestionar esa base de datos utilizando MongoDB.

    A lo largo de la práctica aprenderéis a crear documentos, consultar información, modificar promesas incumplidas y eliminar registros obsoletos.

    Todo ello mediante la terminal y sin necesidad de desarrollar una aplicación web.

    Objetivos de aprendizaje

    Al finalizar, el alumno deberá ser capaz de:

    • Comprender qué es MongoDB y en qué se diferencia de MySQL.
    • Crear y administrar una base de datos NoSQL.
    • Comprender las colecciones y los documentos JSON/BSON.
    • Insertar uno o varios documentos.
    • Realizar consultas con filtros y operadores.
    • Modificar documentos existentes.
    • Eliminar registros.
    • Realizar consultas sencillas de ordenación y estadísticas.
    • Comprender el significado de CRUD.

    2. ¿Qué es MongoDB?

    MongoDB es una base de datos NoSQL orientada a documentos.

    En una base de datos tradicional como MySQL almacenamos información en tablas formadas por filas y columnas. En MongoDB trabajamos con colecciones de documentos que tienen una estructura similar a JSON.

    MySQLTablas y filas

    MongoDBColecciones y documentos

    MySQLMongoDB
    Base de datosBase de datos
    TablaColección
    FilaDocumento
    ColumnaCampo
    Clave primaria_id
    SQLAPI de consultas MongoDB

    Por ejemplo, una promesa política podría almacenarse así:

    
    {
      "politico": "Pepe Promesas",
      "partido": "Partido del Unicornio",
      "promesa": "WiFi gratis hasta en los ascensores",
      "presupuesto": 250000,
      "cumplida": false,
      "etiquetas": ["tecnologia", "internet"]
    }
    

    Observad que tenemos cadenas de texto, números, valores booleanos y arrays dentro de un mismo documento.

    MongoDB almacena los documentos internamente en BSON, una representación binaria que admite tipos adicionales a los de JSON.

    3. Preparación del laboratorio

    Utilizaremos Docker para no tener que instalar directamente el servidor MongoDB en cada máquina virtual.

    Los comandos funcionarán en Linux y también en Windows desde PowerShell, siempre que Docker esté instalado y en ejecución.

    Paso 1. Comprobar Docker

    Abrimos la terminal y ejecutamos:

    
    docker --version
    docker ps
    

    Si Docker está funcionando, podremos consultar la versión y los contenedores activos.

    Paso 2. Descargar MongoDB

    
    docker pull mongo:8.0
    

    Usaremos la imagen oficial de MongoDB 8.0, evitando que una actualización automática de latest cambie la versión durante la práctica.

    Paso 3. Crear nuestro contenedor

    
    docker run -d \
      --name ministerio-mongo \
      -v ministerio_datos:/data/db \
      mongo:8.0
    

    En este comando:

    • -d ejecuta el contenedor en segundo plano.
    • --name identifica nuestro contenedor.
    • -v crea un volumen persistente para almacenar la base de datos.
    • mongo:8.0 es la imagen que estamos utilizando.

    Para esta práctica accederemos a MongoDB directamente desde dentro del contenedor. No necesitamos publicar el puerto 27017 ni dejar la base de datos accesible desde la red.

    Importante: el servidor se utiliza sin autenticación únicamente dentro de este laboratorio aislado. No es una configuración adecuada para producción.

    Paso 4. Comprobar el funcionamiento

    
    docker ps
    

    Deberíamos observar un contenedor llamado ministerio-mongo en estado Up.

    Paso 5. Entrar en MongoDB

    
    docker exec -it ministerio-mongo mongosh
    

    Se abrirá la consola interactiva de MongoDB, llamada mongosh.

    A partir de ahora, los comandos que escribamos dentro de ella son instrucciones de MongoDB, no comandos Bash.

    Podemos comprobar la conexión:

    
    db.runCommand({ ping: 1 })
    

    El resultado debería incluir:

    
    { "ok": 1 }
    

    Referencia: Documentación de la imagen oficial de MongoDB. Docker Hub+1

    4. Crear nuestra primera base de datos

    El Ministerio necesita una base de datos donde registrar sus promesas.

    Escribimos dentro de mongosh:

    
    use ministerio
    

    Podemos comprobar qué base de datos estamos utilizando:

    
    db.getName()
    

    Resultado:

    ministerio

    Ahora creamos una colección llamada promesas.

    
    db.createCollection("promesas")
    

    Comprobamos las colecciones:

    
    show collections
    

    Y para visualizar las bases de datos que realmente tienen datos almacenados:

    
    show dbs
    

    Concepto importante: en MongoDB, seleccionar una base de datos con use no significa que ya esté creada físicamente. Se materializa cuando almacenamos información, como al crear nuestra primera colección.

    5. CREATE — Insertar documentos

    Primera operación CRUD: CREATE

    El Ministro de Transformación Digital, Pepe Promesas, ha anunciado su primera medida electoral:

    *«Todos los ciudadanos tendrán WiFi gratuito, incluso en los ascensores y en las cuevas». *

    Necesitamos registrar esta promesa en nuestra base de datos.

    Paso 1. Insertar un documento

    
    db.promesas.insertOne({
      politico: "Pepe Promesas",
      partido: "Partido del Unicornio",
      promesa: "WiFi gratis hasta en los ascensores",
      categoria: "Tecnologia",
      presupuesto: 250000,
      votos: 1200,
      cumplida: false
    })
    

    El resultado mostrará una confirmación similar a esta:

    {
      acknowledged: true,
      insertedId: ObjectId('...')
    }

    El campo insertedId contiene el identificador único asignado al documento.

    MongoDB genera automáticamente _id cuando no proporcionamos uno.

    Paso 2. Insertar varios documentos

    El Ministerio acaba de celebrar una reunión extraordinaria. Como era de esperar, han aparecido numerosas promesas nuevas.

    Vamos a introducirlas todas de golpe.

    
    db.promesas.insertMany([
      {
        politico: "Lola Subsidios",
        partido: "Union del Bocadillo",
        promesa: "Bocadillos gratis todos los viernes",
        categoria: "Alimentacion",
        presupuesto: 800000,
        votos: 4500,
        cumplida: false
      },
      {
        politico: "Paco Impuestos",
        partido: "Partido del Unicornio",
        promesa: "Eliminar todos los impuestos los lunes",
        categoria: "Economia",
        presupuesto: 5000000,
        votos: 9000,
        cumplida: false
      },
      {
        politico: "Rosa Aeropuertos",
        partido: "Frente de las Rotondas",
        promesa: "Construir un aeropuerto en cada pueblo",
        categoria: "Infraestructuras",
        presupuesto: 15000000,
        votos: 3200,
        cumplida: false
      },
      {
        politico: "Manolo Digital",
        partido: "Union del Bocadillo",
        promesa: "Sustituir funcionarios por robots",
        categoria: "Tecnologia",
        presupuesto: 3000000,
        votos: 11000,
        cumplida: true
      },
      {
        politico: "Carmen Futuro",
        partido: "Frente de las Rotondas",
        promesa: "Construir un carril bici hasta la Luna",
        categoria: "Infraestructuras",
        presupuesto: 50000000,
        votos: 1500,
        cumplida: false
      },
      {
        politico: "Toni Fiestas",
        partido: "Partido del Unicornio",
        promesa: "Declarar los lunes festivos",
        categoria: "Trabajo",
        presupuesto: 2000000,
        votos: 18000,
        cumplida: true
      },
      {
        politico: "Susana Pantallas",
        partido: "Union del Bocadillo",
        promesa: "Una pantalla gigante en cada rotonda",
        categoria: "Tecnologia",
        presupuesto: 650000,
        votos: 2700,
        cumplida: false
      }
    ])
    

    Ya tenemos ocho promesas electorales registradas, contando la primera.

    Paso 3. Contar los documentos

    
    db.promesas.countDocuments({})
    

    Resultado esperado:

    8

    Comprueba que lo has entendido

    ¿Qué diferencia existe entre insertOne() e insertMany()? ¿Qué ocurre si ejecutamos dos veces la misma inserción sin especificar un identificador?

    La segunda inserción generaría nuevos documentos con identificadores diferentes, creando registros duplicados en cuanto a contenido.

    6. READ — Consultar documentos

    Segunda operación CRUD: READ

    El Ministerio necesita consultar sus promesas. Los políticos empiezan a preguntar qué habían prometido durante la campaña.

    Paso 1. Mostrar todas las promesas

    
    db.promesas.find()
    

    El método find() devuelve los documentos de una colección.

    También podemos utilizar:

    
    db.promesas.find().toArray()
    

    Esto permite ver los resultados materializados como un array en la consola, algo cómodo para conjuntos pequeños.

    Paso 2. Buscar un político concreto

    Queremos saber qué prometió Paco Impuestos:

    
    db.promesas.find({
      politico: "Paco Impuestos"
    })
    

    El resultado será el documento correspondiente a Paco.

    Paso 3. Buscar promesas incumplidas

    
    db.promesas.find({
      cumplida: false
    })
    

    Deberían aparecer seis promesas.

    Ahora buscamos las cumplidas:

    
    db.promesas.find({
      cumplida: true
    })
    

    Deberían aparecer dos.

    Paso 4. Consultar por presupuestos

    El Tribunal de Cuentas ha solicitado todas las promesas cuyo presupuesto supera un millón de euros.

    Utilizaremos el operador $gt, que significa greater than (mayor que).

    
    db.promesas.find({
      presupuesto: { $gt: 1000000 }
    })
    

    Otros operadores importantes:

    OperadorSignificado
    $gtMayor que
    $gteMayor o igual que
    $ltMenor que
    $lteMenor o igual que
    $eqIgual que
    $neDistinto de
    $inIncluido en una lista

    Paso 5. Combinar condiciones

    Queremos conocer las promesas de tecnología que siguen incumplidas.

    
    db.promesas.find({
      categoria: "Tecnologia",
      cumplida: false
    })
    

    Cuando incluimos varias condiciones en un mismo filtro, MongoDB aplica un AND implícito.

    En este caso aparecerán las promesas de Pepe Promesas y Susana Pantallas.

    Paso 6. Utilizar OR

    El Ministerio quiere localizar las promesas que pertenezcan a las categorías de Economía o Trabajo.

    
    db.promesas.find({
      $or: [
        { categoria: "Economia" },
        { categoria: "Trabajo" }
      ]
    })
    

    Paso 7. Mostrar solamente algunos campos

    No siempre necesitamos visualizar todos los campos de cada documento.

    Queremos mostrar solamente el nombre del político, su promesa y el presupuesto.

    
    db.promesas.find(
      {},
      {
        _id: 0,
        politico: 1,
        promesa: 1,
        presupuesto: 1
      }
    )
    

    El 1 indica que queremos incluir un campo. El 0 indica que deseamos excluirlo.

    En este ejemplo también ocultamos _id, que se muestra por defecto.

    Paso 8. Ordenar los resultados

    Queremos descubrir cuáles son las promesas más caras.

    
    db.promesas.find(
      {},
      { _id: 0, politico: 1, presupuesto: 1 }
    ).sort({ presupuesto: -1 })
    

    En la ordenación:

    • 1 significa ascendente.
    • -1 significa descendente.

    Paso 9. Limitar resultados

    El Ministerio solamente quiere las tres promesas más caras.

    
    db.promesas.find(
      {},
      { _id: 0, politico: 1, presupuesto: 1 }
    ).sort({ presupuesto: -1 }).limit(3)
    

    El resultado esperado es:

    PolíticoPresupuesto
    Carmen Futuro50.000.000 €
    Rosa Aeropuertos15.000.000 €
    Paco Impuestos5.000.000 €

    7. UPDATE — Modificar documentos

    Tercera operación CRUD: UPDATE

    Han pasado varios meses y el Ministerio necesita actualizar la situación de sus promesas.

    Paso 1. Modificar un documento

    Pepe Promesas asegura que el WiFi ya funciona en todos los ascensores.

    Aunque nadie lo ha podido comprobar, el Ministerio decide marcar la promesa como cumplida.

    
    db.promesas.updateOne(
      { politico: "Pepe Promesas" },
      { $set: { cumplida: true } }
    )
    

    El operador $set permite cambiar uno o varios campos.

    La respuesta habitual incluye:

    matchedCount: 1
    modifiedCount: 1

    matchedCount indica cuántos documentos coincidieron con el filtro, y modifiedCount cuántos fueron modificados.

    Para comprobarlo:

    
    db.promesas.findOne({
      politico: "Pepe Promesas"
    })
    

    Paso 2. Modificar varios campos

    El Gobierno descubre que el carril bici lunar tendrá un coste mayor del previsto.

    
    db.promesas.updateOne(
      { politico: "Carmen Futuro" },
      {
        $set: {
          presupuesto: 75000000,
          cumplida: false
        }
      }
    )
    

    Paso 3. Incrementar un valor numérico

    Durante una manifestación política, Paco Impuestos ha ganado 500 votos adicionales.

    En lugar de consultar los votos actuales y escribir el nuevo total, utilizamos $inc.

    
    db.promesas.updateOne(
      { politico: "Paco Impuestos" },
      { $inc: { votos: 500 } }
    )
    

    Si tenía 9.000 votos, ahora tendrá 9.500.

    También podemos restar:

    
    db.promesas.updateOne(
      { politico: "Paco Impuestos" },
      { $inc: { votos: -200 } }
    )
    

    Ahora tendrá 9.300.

    Paso 4. Actualizar varios documentos

    El Ministerio decide incrementar en un 10 % el presupuesto de todas las promesas tecnológicas.

    
    db.promesas.updateMany(
      { categoria: "Tecnologia" },
      { $mul: { presupuesto: 1.10 } }
    )
    

    updateMany() modifica todos los documentos que cumplan la condición. $mul multiplica el valor numérico por el factor indicado.

    Paso 5. Incorporar un campo nuevo

    El Ministerio ha creado un departamento de excusas.

    Ahora cada promesa necesita un campo llamado excusa.

    
    db.promesas.updateOne(
      { politico: "Rosa Aeropuertos" },
      {
        $set: {
          excusa: "No encontramos suficientes pilotos"
        }
      }
    )
    

    Este campo no existía anteriormente en el documento. MongoDB permite añadirlo sin alterar los demás documentos de la colección.

    Este ejemplo ilustra una de las características de MongoDB: su modelo flexible de documentos.

    8. DELETE — Eliminar documentos

    Cuarta operación CRUD: DELETE

    Tras una auditoría interna, el Ministerio decide eliminar algunas promesas de su registro.

    Paso 1. Eliminar una promesa

    Se ha decidido cancelar el proyecto de las pantallas en las rotondas.

    
    db.promesas.deleteOne({
      politico: "Susana Pantallas"
    })
    

    El resultado esperado incluye:

    deletedCount: 1

    Comprobamos que ha desaparecido:

    
    db.promesas.find({
      politico: "Susana Pantallas"
    })
    

    No debería devolver ningún documento.

    Paso 2. Eliminar varios documentos

    El Ministerio ha decidido eliminar todas las promesas con presupuestos inferiores a 300.000 €.

    Primero consultaremos los documentos afectados:

    
    db.promesas.find({
      presupuesto: { $lt: 300000 }
    })
    

    En este punto de nuestra práctica, la única promesa que cumple esa condición es la de Pepe Promesas, cuyo presupuesto se incrementó hasta 275.000 €.

    Ahora sí, ejecutamos:

    
    db.promesas.deleteMany({
      presupuesto: { $lt: 300000 }
    })
    

    Advertencia: una operación deleteMany({}) sin filtros eliminaría todos los documentos de la colección. Debemos comprobar siempre la condición antes de ejecutarla.

    Paso 3. Comprobar el resultado

    
    db.promesas.countDocuments({})
    

    Si hemos seguido todos los pasos en orden, tendremos seis documentos.

    9. Resumen visual de CRUD

    10. Consultas adicionales — El escándalo de los presupuestos

    Hasta ahora hemos aprendido las cuatro operaciones fundamentales. Pero MongoDB también permite realizar operaciones de análisis.

    El Ministerio quiere conocer algunos datos estadísticos sobre sus promesas.

    ¿Cuánto dinero ha prometido el Gobierno?

    Utilizaremos una operación de agregación:

    
    db.promesas.aggregate([
      {
        $group: {
          _id: null,
          presupuestoTotal: { $sum: "$presupuesto" }
        }
      }
    ])
    

    $group agrupa documentos y $sum suma los valores del campo indicado.

    ¿Cuántas promesas tiene cada partido?

    
    db.promesas.aggregate([
      {
        $group: {
          _id: "$partido",
          totalPromesas: { $sum: 1 }
        }
      },
      {
        $sort: { totalPromesas: -1 }
      }
    ])
    

    Esto es una introducción a las aggregation pipelines. No necesitamos profundizar en ellas todavía, pero sí comprender que MongoDB puede hacer algo más que buscar documentos.

    11. Reto individual — La campaña electoral del alumno

    Misión: Operación Elecciones 404

    Actividad autónoma · 30–40 minutos · Sin soluciones

    El Ministerio ha decidido convocar nuevas elecciones. Cada alumno deberá crear su propio partido político ficticio y registrar sus promesas absurdas utilizando MongoDB.

    Primera parte: creación del partido

    El alumno debe crear una nueva colección llamada elecciones.

    En ella registrará al menos cinco promesas inventadas por él. Cada documento deberá contener:

    • Nombre del político.
    • Partido político.
    • Promesa electoral.
    • Categoría.
    • Presupuesto.
    • Número de votos.
    • Estado de cumplimiento.
    • Un campo adicional de su elección.

    Los nombres y promesas deben ser ficticios y utilizar el humor o la sátira.

    Segunda parte: operaciones obligatorias

    Lista de comprobación

    0 de 13Crear la colección elecciones.Insertar cinco promesas con insertMany().Mostrar todas las promesas.Buscar las promesas de un partido concreto.Encontrar las promesas cuyo presupuesto supere 1.000.000 €.Mostrar solamente los campos político, promesa y presupuesto.Ordenar las promesas de mayor a menor presupuesto.Mostrar únicamente las dos promesas más votadas.Modificar una promesa para marcarla como cumplida.Incrementar los votos de un político en 1.000.Añadir un campo llamado excusa a un documento.Eliminar una promesa utilizando deleteOne().Mostrar cuántos documentos quedan en la colección.

    Reiniciar comprobación

    Tercera parte: reto adicional

    Los alumnos que terminen antes podrán intentar tres operaciones más sin recibir los comandos directamente:

    1. Calcular el presupuesto total de todas las promesas.
    2. Averiguar qué partido tiene más promesas registradas.
    3. Mostrar únicamente las promesas incumplidas y ordenarlas por número de votos.

    12. Entrega del trabajo

    El alumno deberá preparar un archivo README.md que incluya una breve explicación de la práctica, los comandos utilizados y las capturas de las operaciones principales.

    No es necesario entregar una aplicación web. El objetivo es demostrar que sabe trabajar directamente con MongoDB desde la consola.

    Criterios de evaluación

    ApartadoPuntos
    Creación de BD y colecciones1
    Inserción de documentos2
    Consultas y filtros3
    Actualización de documentos2
    Eliminación de documentos1
    Documentación y explicaciones1
    Total10

    13. Cierre y limpieza del laboratorio

    Para salir de la consola de MongoDB:

    
    exit
    

    Ya en la terminal Linux o PowerShell, podemos detener el contenedor:

    
    docker stop ministerio-mongo
    

    Para volver a utilizarlo otro día:

    
    docker start ministerio-mongo
    docker exec -it ministerio-mongo mongosh
    

    Gracias al volumen ministerio_datos, nuestra información seguirá almacenada aunque detengamos el contenedor.

    Si queremos eliminar el contenedor al terminar:

    
    docker rm -f ministerio-mongo
    

    El volumen seguirá existiendo. Para eliminar también todos los datos, de forma irreversible:

    
    docker volume rm ministerio_datos
    

    14. Preguntas finales para reflexionar

    Antes de entregar, cada alumno deberá contestar brevemente:

    1. ¿Qué diferencia existe entre una tabla de MySQL y una colección de MongoDB?
    2. ¿Para qué sirve el campo _id?
    3. ¿Qué diferencia hay entre find() y findOne()?
    4. ¿Cuándo utilizarías updateOne() en lugar de updateMany()?
    5. ¿Qué ocurriría si ejecutases deleteMany({})?
    6. ¿Por qué puede resultar útil que dos documentos de una colección tengan campos diferentes?
    7. ¿Para qué nos sirve utilizar un volumen Docker en esta práctica?
  • Reto 1 – Arsenal de Herramientas Web de Ciberseguridad

    Reto 1 – Arsenal de Herramientas Web de Ciberseguridad

    Sois el equipo de inteligencia de una corporación tipo Weyland-Yutani / Tyrell / Cyberdyne. Necesitáis un arsenal organizado de herramientas online de ciberseguridad para elegir la herramienta adecuada en cada misión. Vuestra tarea es diseñar y explotar la base de datos de ese arsenal.

    Crear una base de datos llamada, por ejemplo, ciber_arsenal, que almacene herramientas web de ciberseguridad / OSINT / análisis:

    • SELECT básicos
    • filtros (WHERE)
    • ordenaciones (ORDER BY)
    • agregaciones (COUNT, AVG…)
    • GROUP BY / HAVING
    • JOIN entre varias tablas
    • subconsultas sencillas

    1. Diseño de la base de datos

    Tablas

    3 tablas principales:

    1. categorias – Tipo de herramienta (OSINT, análisis malware, escaneo, forense…).
    2. herramientas – Cada herramienta web concreta.
    3. tags y herramienta_tag – Muchos-a-muchos y JOINs.

    1.1. Tabla categorias

    CREATE DATABASE ciber_arsenal;
    USE ciber_arsenal;
    
    CREATE TABLE categorias (
        id_categoria      INT AUTO_INCREMENT PRIMARY KEY,
        nombre            VARCHAR(50) NOT NULL,
        descripcion       VARCHAR(255)
    );
    

    Ejemplos de categorías:

    • OSINT
    • Escaneo de puertos / servicios
    • Análisis de malware
    • Análisis de dominios / DNS
    • Forense
    • Threat Intelligence

    1.2. Tabla herramientas

    CREATE TABLE herramientas (
        id_herramienta        INT AUTO_INCREMENT PRIMARY KEY,
        nombre                VARCHAR(100) NOT NULL,
        url                   VARCHAR(255) NOT NULL,
        id_categoria          INT,
        tipo_acceso           ENUM('web','api','desktop','mixto') DEFAULT 'web',
        licencia              ENUM('gratuita','freemium','comercial','código abierto') DEFAULT 'gratuita',
        requiere_registro     BOOLEAN DEFAULT 0,
        nivel_criticidad      TINYINT,      -- 1 a 5: 1 = muy suave, 5 = muy delicada
        pais_servidor         VARCHAR(50),  -- País principal (aprox)
        anyo_lanzamiento      INT,
        uso_principal         VARCHAR(100), -- en una frase corta
        requiere_vpn          BOOLEAN DEFAULT 0,
        notas                 VARCHAR(255),
        CONSTRAINT fk_cat
            FOREIGN KEY (id_categoria) REFERENCES categorias(id_categoria)
    );
    

    Con estos campos puedes preguntar por:

    • Herramientas por país
    • Por tipo de licencia
    • Por nivel de criticidad
    • Por año de lanzamiento
    • etc.

    1.3. Tablas de tags

    CREATE TABLE tags (
        id_tag      INT AUTO_INCREMENT PRIMARY KEY,
        nombre      VARCHAR(50) NOT NULL
    );
    
    CREATE TABLE herramienta_tag (
        id_herramienta INT,
        id_tag         INT,
        PRIMARY KEY (id_herramienta, id_tag),
        FOREIGN KEY (id_herramienta) REFERENCES herramientas(id_herramienta),
        FOREIGN KEY (id_tag) REFERENCES tags(id_tag)
    );
    

    Ejemplos de tags: shodan-like, buscador_ips, malware_sandbox, dns, breach, wifi, mobile, etc.

    Datos de EJEMPLO

    Categorías

    INSERT INTO categorias (nombre, descripcion) VALUES
    ('OSINT', 'Recolección de información en fuentes abiertas'),
    ('Escaneo', 'Descubrimiento de puertos, servicios y hosts'),
    ('Analisis Malware', 'Análisis de ejecutables sospechosos'),
    ('Analisis Dominios/DNS', 'Información sobre dominios, DNS y certificados'),
    ('Forense', 'Análisis forense de sistemas y archivos'),
    ('Threat Intelligence', 'Reputación de IPs, dominios y campañas');
    

    Herramientas

    INSERT INTO herramientas 
    (nombre, url, id_categoria, tipo_acceso, licencia, requiere_registro, nivel_criticidad, pais_servidor, anyo_lanzamiento, uso_principal, requiere_vpn, notas)
    VALUES
    ('Shodan', 'https://www.shodan.io', 1, 'web', 'freemium', 1, 5, 'Estados Unidos', 2009, 'Buscador de dispositivos conectados a Internet', 0, 'API muy utilizada en pentesting'),
    ('Censys', 'https://search.censys.io', 1, 'web', 'freemium', 1, 5, 'Estados Unidos', 2015, 'Escaneo y búsqueda de hosts y certificados', 0, 'Similar a Shodan'),
    ('ZoomEye', 'https://www.zoomeye.org', 1, 'web', 'freemium', 1, 5, 'China', 2013, 'Buscador de servicios y dispositivos expuestos', 1, 'A veces bloqueado en algunos países'),
    ('VirusTotal', 'https://www.virustotal.com', 3, 'web', 'freemium', 1, 4, 'España', 2004, 'Análisis de ficheros y URLs con múltiples antivirus', 0, 'Muy usado para análisis rápido'),
    ('Hybrid Analysis', 'https://www.hybrid-analysis.com', 3, 'web', 'gratuita', 1, 4, 'Estados Unidos', 2014, 'Sandbox para analizar malware', 0, 'Requiere cuenta gratuita'),
    ('Any.Run', 'https://any.run', 3, 'web', 'freemium', 1, 4, 'Emiratos Árabes Unidos', 2016, 'Sandbox interactiva para malware', 0, 'Permite ver la ejecución en tiempo real'),
    ('Urlscan.io', 'https://urlscan.io', 4, 'web', 'gratuita', 0, 3, 'Alemania', 2017, 'Análisis de URLs y recursos cargados', 0, 'Muy útil para phishing'),
    ('crt.sh', 'https://crt.sh', 4, 'web', 'gratuita', 0, 2, 'Estados Unidos', 2014, 'Búsqueda de certificados TLS emitidos', 0, 'Muy útil para descubrir subdominios'),
    ('SecurityTrails', 'https://securitytrails.com', 4, 'web', 'freemium', 1, 3, 'Estados Unidos', 2017, 'Inteligencia sobre dominios, DNS e IPs', 0, 'Buen complemento a otras OSINT'),
    ('Have I Been Pwned', 'https://haveibeenpwned.com', 6, 'web', 'gratuita', 0, 3, 'Australia', 2013, 'Comprobación de correos en brechas de datos', 0, 'Tiene API limitada'),
    ('Dehashed', 'https://www.dehashed.com', 6, 'web', 'comercial', 1, 5, 'Estados Unidos', 2015, 'Búsqueda avanzada en bases de datos filtradas', 1, 'Muy potente pero de pago'),
    ('MalwareBazaar', 'https://bazaar.abuse.ch', 3, 'web', 'gratuita', 0, 4, 'Suiza', 2019, 'Repositorio de muestras de malware', 1, 'Uso muy delicado'),
    ('AbuseIPDB', 'https://www.abuseipdb.com', 6, 'web', 'freemium', 1, 3, 'Irlanda', 2016, 'Reputación de IPs reportadas por abuso', 0, 'Útil para filtrar tráfico malicioso'),
    ('Wigle', 'https://wigle.net', 1, 'web', 'gratuita', 1, 3, 'Estados Unidos', 2001, 'Base de datos de redes WiFi geolocalizadas', 1, 'Muy útil para geolocalización'),
    ('Archive.org', 'https://web.archive.org', 1, 'web', 'gratuita', 0, 1, 'Estados Unidos', 1996, 'Archivo histórico de páginas web', 0, 'Wayback Machine'),
    ('MetaDefender Cloud', 'https://metadefender.opswat.com', 3, 'web', 'gratuita', 0, 3, 'Estados Unidos', 2017, 'Análisis de ficheros y URLs', 0, 'Alternativa a VirusTotal');
    

    Tags

    INSERT INTO tags (nombre) VALUES
    ('buscador_ips'),
    ('buscador_dispositivos'),
    ('malware_sandbox'),
    ('analisis_url'),
    ('breach'),
    ('dns'),
    ('dominios'),
    ('certificados'),
    ('threat_intel'),
    ('wifi'),
    ('historial_web');
    

    Relación herramienta-tag

    -- Shodan
    INSERT INTO herramienta_tag VALUES (1, 1), (1, 2), (1, 9);
    -- Censys
    INSERT INTO herramienta_tag VALUES (2, 1), (2, 2), (2, 8), (2, 9);
    -- ZoomEye
    INSERT INTO herramienta_tag VALUES (3, 1), (3, 2);
    -- VirusTotal
    INSERT INTO herramienta_tag VALUES (4, 3), (4, 4), (4, 9);
    -- Hybrid Analysis
    INSERT INTO herramienta_tag VALUES (5, 3), (5, 9);
    -- Any.Run
    INSERT INTO herramienta_tag VALUES (6, 3);
    -- Urlscan.io
    INSERT INTO herramienta_tag VALUES (7, 4), (7, 9);
    -- crt.sh
    INSERT INTO herramienta_tag VALUES (8, 6), (8, 7), (8, 8);
    -- SecurityTrails
    INSERT INTO herramienta_tag VALUES (9, 6), (9, 7), (9, 9);
    -- HIBP
    INSERT INTO herramienta_tag VALUES (10, 5), (10, 9);
    -- Dehashed
    INSERT INTO herramienta_tag VALUES (11, 5), (11, 9);
    -- MalwareBazaar
    INSERT INTO herramienta_tag VALUES (12, 3), (12, 9);
    -- AbuseIPDB
    INSERT INTO herramienta_tag VALUES (13, 1), (13, 9);
    -- Wigle
    INSERT INTO herramienta_tag VALUES (14, 10);
    -- Archive.org
    INSERT INTO herramienta_tag VALUES (15, 11);
    -- MetaDefender Cloud
    INSERT INTO herramienta_tag VALUES (16, 3), (16, 4);
    

    MISION

    Vuestra misión es diseñar, crear y explotar una base de datos que almacene un arsenal de herramientas web utilizadas en ciberseguridad (OSINT, análisis de malware, dominios, threat intel, etc.).

    1. Crear la base de datos ciber_arsenal.
    2. Crear las tablas categorias, herramientas, tags y herramienta_tag siguiendo el modelo proporcionado.
    3. Insertar al menos 15 herramientas con datos realistas (podéis completar o modificar los ejemplos).
    4. Diseñar al menos 10 tags y relacionarlos con las herramientas.
    5. Realizar una colección de consultas SQL (SELECT) para responder a preguntas típicas de un analista de ciberseguridad.


    Propuesta de ejercicios de consultas SELECT

    1. Mostrar todas las herramientas con su nombre y URL.
    2. Listar todas las herramientas ordenadas por año de lanzamiento (más antiguas primero).
    3. Mostrar nombre, licencia y categoría de todas las herramientas que sean gratuitas.
    4. Mostrar las herramientas cuya licencia sea “freemium” o “comercial”.
    5. Listar herramientas que no requieran registro (requiere_registro = 0).
    6. Mostrar las herramientas con nivel de criticidad mayor o igual que 4.
    7. Contar cuántas herramientas hay por cada categoría (usar GROUP BY).
    8. Obtener el número total de herramientas por tipo de licencia.
    9. Calcular el año medio de lanzamiento de las herramientas de la categoría “OSINT”.
    10. Listar todas las herramientas cuyo país del servidor sea “Estados Unidos”.
    11. Listar las herramientas que requieren VPN (requiere_vpn = 1).
    12. Mostrar todas las herramientas cuya URL contenga la palabra "virus" o "malware".
    13. Listar las herramientas lanzadas después de 2015 y que sean de tipo acceso web.
    14. Mostrar el nombre de la categoría y el número de herramientas asociadas, ordenado de mayor a menor número.
    15. Listar todas las herramientas junto con el nombre de su categoría (JOIN entre herramientas y categorias).
    16. Listar las herramientas que tengan el tag "malware_sandbox" (JOIN con tags y herramienta_tag).
    17. Mostrar todas las herramientas que tengan el tag "breach", indicando nombre de la herramienta, URL y licencia.
    18. Listar las herramientas que tengan más de 2 tags asociados (JOIN + GROUP BY + HAVING).
    19. Encontrar las herramientas con nivel de criticidad 5 que además tengan algún tag relacionado con amenazas ('threat_intel' o 'breach').
    20. Usar una subconsulta para mostrar todas las herramientas cuya categoría tenga más de 3 herramientas asociadas (subconsulta sobre categorias o herramientas).
    21. Mostrar las herramientas cuya licencia sea distinta de la mayoría (por ejemplo, todas las que NO sean gratuitas si la mayoría lo son).
    22. Mostrar, por cada país, cuántas herramientas tiene y el nivel de criticidad medio.
    23. Crear una consulta que responda a: “Quiero herramientas para investigar dominios o DNS que sean gratuitas y no requieran VPN”.
    24. Crear una consulta que responda a: “Necesito un sustituto de Shodan que no esté alojado en Estados Unidos”.
    25. Crear una consulta que devuelva una “lista recomendada”: herramientas con criticidad entre 2 y 4, gratuitas o freemium, y con al menos un tag.