lunes, 8 de junio de 2026

LINQ: operaciones comunes

Continuamos con esta serie sobre LINQ y la lectura de archivos XML y JSON. Ahora profundizaremos un poco con las operaciones básicas que podemos realizar:

  • Where: Filtrar. 
  • Select: Proyecta campos. 
  • OrderBy: Ordenar. 
  • GroupBy: Agrupar y contar. 
  • Join: Unir colecciones. 
  • Any: Comprobar existencia. 
  • All: Comprobar condición en todos. 
  • Count: Devolver cantidad.

Consultas en archivos XML con LINQ

Realizaremos algunas consultas teniendo como entrada el siguiente archivo XML:

empleados.xml

<?xml version="1.0" encoding="UTF-8"?>
<Empleados>
<Empleado> 
<Id>4392552</Id>
<Nombre>Horacio</Nombre>
<Apellidos>Gomez Torres</Apellidos>
<Correo>horacio.gomez.tor@infotec.com</Correo>
<Salario>25000.00</Salario>
<Departamento>202</Departamento>
</Empleado>

<Empleado>
<Id>4292856</Id>
<Nombre>Veronica</Nombre>
<Apellidos>Uribe Gomora</Apellidos>
<Correo>veronica.uribe.gom@infotec.com</Correo>
<Salario>30000.00</Salario>
<Departamento>204</Departamento>
</Empleado>


<Empleado>
<Id>4701330</Id>
<Nombre>Karla</Nombre>
<Apellidos>Perez Perez</Apellidos>
<Correo>karla.perez.per@infotec.com</Correo>
<Salario>28000.00</Salario>
<Departamento>200</Departamento>
</Empleado>

<Empleado>
<Id>4701366</Id>
<Nombre>Juan</Nombre>
<Apellidos>Archundia Lara</Apellidos>
<Correo>juan.archundia.lar@infotec.com</Correo>
<Salario>38000.00</Salario>
<Departamento>204</Departamento>
</Empleado>

</Empleados>

Mapearemos las etiquetas XML en una clase C#.

Empleado.cs

using System.Xml.Serialization;

namespace model
{
    public class Empleado
    {
        public string Id { get; set; } = string.Empty;
        public string Nombre { get; set; } = string.Empty;
        public string Apellidos { get; set; } = string.Empty;
        public string Correo { get; set; } = string.Empty;
        public decimal Salario { get; set; } = decimal.Zero;
        public string Departamento { get; set; } = string.Empty;
    }

    [XmlRoot("Empleados")]
    public class Empleados
    {
        [XmlElement("Empleado")]
        public List<Empleado> Lista { get; set; }
    }
}

En una entrega anterior vimos cómo consultar documentos XML.

El siguiente bloque es importante para serializar los datos a partir de un documento XML.

// Ruta del archivo XML
 public static readonly string PATH_XML = "empleados.xml";

// Serializar datos
var serializer = new XmlSerializer(typeof(Empleados));

// Lectura del XML
using var reader = new StreamReader(PATH_XML);

Comenzaremos con la operación Where:

          Console.WriteLine("Empleados mejor pagados:");
             var empleadosBienPagados = from emp in empleados.Lista
                               where emp.Salario > 28000
                               select emp;
            foreach (var emp in empleadosBienPagados)
            {
                Console.WriteLine($"{emp.Nombre} - {emp.Salario}");
            }

En SQL sería:

SELECT *
FROM empleados
WHERE Salario > 28000;

Continuamos con la operación Select:

           Console.WriteLine("Nombre y correo:");
            var contactosDev = from dev in empleados.Lista
                   select new { dev.Nombre, dev.Correo };

            foreach (var emp in contactosDev)
            {
                Console.WriteLine($"{emp.Nombre} - {emp.Correo}");
            }

En SQL sería:

SELECT Nombre, Correo
FROM empleados;

Continuamos con la operación OrderBy:

           Console.WriteLine("Empleados [por salario ascendente]:");
            var empleadosOrdenados = from emp in empleados.Lista
                         orderby emp.Salario
                         select emp;
            foreach (var emp in empleadosOrdenados)
            {
                Console.WriteLine($"{emp.Nombre} {emp.Apellidos}, Depto: {emp.Departamento} , Salario: {emp.Salario}");
            }

En SQL sería:

SELECT Nombre, Apellidos, Departamento, Salario
FROM empleados
ORDER BY Salario ASC;

Continuamos con la operación GroupBy y Count:

             Console.WriteLine("Agrupar empleados por departamento:");
            var empleadosPorDepto = from emp in empleados.Lista
                        group emp by emp.Departamento into deptGroup
                        select new
                        {
                            Departamento = deptGroup.Key,
                            Cantidad = deptGroup.Count()
                        };
            foreach (var emp in empleadosPorDepto)
            {
                Console.WriteLine($"Departamento: {emp.Departamento} , Cantidad: {emp.Cantidad}");
            }

En SQL sería:

SELECT Departamento, COUNT(*) AS Cantidad
FROM empleados
GROUP BY Departamento;

Para el conteo (COUNT) sería:

SELECT COUNT(*) AS TotalEmpleados
FROM empleados;

Continuamos con la operación Any:

bool hayAltosSueldos = empleados.Lista.Any(e => e.Salario > 30000);
            if(hayAltosSueldos)
            {
                Console.WriteLine("Existe al menos un empleado con un salario relativamente grande.");
            }

En SQL sería:

SELECT CASE 
         WHEN EXISTS (SELECT 1 FROM empleados WHERE Salario > 30000) 
         THEN 'TRUE' 
         ELSE 'FALSE' 
       END AS HayAltosSueldos;

Finalizamos con la operación All:

bool todosConCorreoEdu = empleados.Lista.All(d => d.Correo.EndsWith("infotec.com"));
            if(todosConCorreoEdu)
            {
                Console.WriteLine("Todos los empleados tienen correo institucional.");
            }

En SQL sería:

SELECT CASE 
         WHEN NOT EXISTS (
              SELECT 1 FROM empleados 
              WHERE Correo NOT LIKE '%infotec.com'
         )
         THEN 'TRUE'
         ELSE 'FALSE'
       END AS TodosConCorreoInstitucional;

Programa completo:

Program.cs

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using model;
using System.Xml.Serialization;

class Program
{
    public static readonly string PATH_XML = "empleados.xml";
    
    static void Main()
    {
        var serializer = new XmlSerializer(typeof(Empleados));
        using var reader = new StreamReader(PATH_XML);
        Empleados empleados = (Empleados)serializer.Deserialize(reader);
        if(empleados != null)
        {
             Console.WriteLine("Empleados mejor pagados:");
             var empleadosBienPagados = from emp in empleados.Lista
                               where emp.Salario > 28000
                               select emp;
            foreach (var emp in empleadosBienPagados)
            {
                Console.WriteLine($"{emp.Nombre} - {emp.Salario}");
            }

            Console.WriteLine("Nombre y correo:");
            var contactosDev = from dev in empleados.Lista
                   select new { dev.Nombre, dev.Correo };

            foreach (var emp in contactosDev)
            {
                Console.WriteLine($"{emp.Nombre} - {emp.Correo}");
            }

            Console.WriteLine("Empleados [por salario ascendente]:");
            var empleadosOrdenados = from emp in empleados.Lista
                         orderby emp.Salario
                         select emp;
            foreach (var emp in empleadosOrdenados)
            {
                Console.WriteLine($"{emp.Nombre} {emp.Apellidos}, Depto: {emp.Departamento} , Salario: {emp.Salario}");
            }

            Console.WriteLine("Agrupar empleados por departamento:");
            var empleadosPorDepto = from emp in empleados.Lista
                        group emp by emp.Departamento into deptGroup
                        select new
                        {
                            Departamento = deptGroup.Key,
                            Cantidad = deptGroup.Count()
                        };
            foreach (var emp in empleadosPorDepto)
            {
                Console.WriteLine($"Departamento: {emp.Departamento} , Cantidad: {emp.Cantidad}");
            }


            bool hayAltosSueldos = empleados.Lista.Any(e => e.Salario > 30000);
            if(hayAltosSueldos)
            {
                Console.WriteLine("Existe al menos un empleado con un salario relativamente grande.");
            }

            bool todosConCorreoEdu = empleados.Lista.All(d => d.Correo.EndsWith("infotec.com"));
            if(todosConCorreoEdu)
            {
                Console.WriteLine("Todos los empleados tienen correo institucional.");
            }

        }

        

    }
}

Construimos y ejecutamos:

$ dotnet build
$ dotnet run

Salida:

Empleados mejor pagados:
Veronica - 30000.00
Juan - 38000.00
Nombre y correo:
Horacio - horacio.gomez.tor@infotec.com
Veronica - veronica.uribe.gom@infotec.com
Karla - karla.perez.per@infotec.com
Juan - juan.archundia.lar@infotec.com
Empleados [por salario ascendente]:
Horacio Gomez Torres, Depto: 202 , Salario: 25000.00
Karla Perez Perez, Depto: 200 , Salario: 28000.00
Veronica Uribe Gomora, Depto: 204 , Salario: 30000.00
Juan Archundia Lara, Depto: 204 , Salario: 38000.00
Agrupar empleados por departamento:
Departamento: 202 , Cantidad: 1
Departamento: 204 , Cantidad: 2
Departamento: 200 , Cantidad: 1
Existe al menos un empleado con un salario relativamente grande.
Todos los empleados tienen correo institucional.

¡Hemos creado consultas básicas con LINQ!

Continuaremos con esta serie sobre C# y LINQ.

Enlaces:

https://codemonkeyjunior.blogspot.com/2026/06/linq-consultando-documentos-json.html
https://codemonkeyjunior.blogspot.com/2025/12/linq-consultando-documentos-xml.html
https://codemonkeyjunior.blogspot.com/2025/11/linq-un-lenguaje-de-consulta-para-c.html

sábado, 6 de junio de 2026

OWASP: el top ten en seguridad y vulnerabilidad

En una entrega anterior vimos las 10 vulnerabilidades de seguridad más críticas en aplicaciones web.

La lista OWASP 2025 es la siguiente:

El top ten de las vulnerabilidades web

Número Vulnerabilidad
A01:2025 Broken Access Control
A02:2025 Security Misconfiguration
A03:2025 Software Supply Chain Failures
A04:2025 Cryptographic Failures
A05:2025 Injection
A06:2025 Insecure Design
A07:2025 Authentication Failures
A08:2025 Software or Data Integrity Failures
A09:2025 Security Logging and Alerting Failures
A10:2025 Mishandling of Exceptional Conditions

La tabla del OWASP Top Ten 2025 no es solo un listado teórico: como desarrollador podemos usarla como guía práctica de control de calidad y seguridad en cada etapa del ciclo de vida de tu software.

Ejemplos del OWASP Top Ten

Broken Access Control (A01)

  • Implementa roles y permisos explícitos en tu código (RBAC/ABAC). 
  • Usa pruebas automatizadas para verificar que un usuario no pueda acceder a recursos fuera de su rol. 
  • Ejemplo: en APIs REST, valida siempre el userId contra el token JWT.

Inseguro:

# Cualquiera puede ver datos de otro usuario
user_id = request.args.get("id")
data = db.get_user(user_id)

Seguro:

# Validar que el usuario autenticado coincide
if user_id == current_user.id:
    data = db.get_user(user_id)

Security Misconfiguration (A02)

  • Mantén configuraciones seguras por defecto (no exponer puertos innecesarios, deshabilitar directory listing). 
  • Automatiza revisiones con herramientas como Docker Bench Security o kube-bench en Kubernetes.

Inseguro:

# Exponer listado de directorios
autoindex on;

Seguro:

# Deshabilitar listado
autoindex off;

Software Supply Chain Failures (A03)

  • Usa dependabot o equivalentes para monitorear librerías vulnerables. 
  • Firma y verifica paquetes (ejemplo: npm audit, sigstore). 
  • Evita dependencias sin mantenimiento activo.

Inseguro:

$ npm install paquete-desconocido

Seguro:

$ npm audit
$ npm install paquete-verificado@1.2.3

Cryptographic Failures (A04)

  • Aplica cifrado fuerte (AES‑256, TLS 1.3). 
  • Nunca inventes tu propio algoritmo: usa librerías probadas. 
  • Rotación periódica de claves y certificados.

Inseguro:

# Cifrado débil
cipher = DES.new(key)

Seguro:

# Cifrado fuerte
cipher = AES.new(key, AES.MODE_GCM)

Injection (A05)

  • Usa ORMs o consultas parametrizadas (LINQ, SQL prepared statements). 
  • Escapa entradas en plantillas HTML/JS. 
  • Haz pruebas de fuzzing para detectar inyecciones.

Inseguro:

cursor.execute("SELECT * FROM users WHERE name = '" + name + "'")

Seguro:

cursor.execute("SELECT * FROM users WHERE name = %s", (name,))

Insecure Design (A06)

  • Aplica Threat Modeling desde el inicio (STRIDE, DFD). 
  • Diseña con principios de seguridad por defecto y mínimo privilegio. 
  • Documenta decisiones arquitectónicas con foco en seguridad.

Inseguro:

# Contraseña en texto plano
user.password = "123456"

Seguro:

# Hash seguro
user.password = bcrypt.hashpw(password, bcrypt.gensalt())

Authentication Failures (A07)

  • Implementa MFA (Multi‑Factor Authentication). 
  • Usa protocolos modernos: OAuth2, OpenID Connect
  • Evita sesiones largas sin expiración.

Inseguro:

# Sesión sin expiración
session['user'] = user.id

Seguro:

session['user'] = user.id
session['expires'] = datetime.now() + timedelta(minutes=30)

Software or Data Integrity Failures (A08)

  • Verifica integridad con hashes y firmas digitales. 
  • Usa pipelines CI/CD con validación de integridad. 
  • Protege contra ataques de deserialización insegura.

Inseguro:

# Ejecutar código sin verificar
exec(downloaded_code)

Seguro:

# Verificar firma digital antes de ejecutar
if verify_signature(downloaded_code, signature):
    exec(downloaded_code)

Security Logging and Alerting Failures (A09)

  • Centraliza logs en sistemas como ELK o Azure Monitor. 
  • Define alertas automáticas para eventos críticos (intentos de login fallidos, escalamiento de privilegios). 
  • Asegura que los logs no contengan datos sensibles en texto plano.

Inseguro:

# No se registra intento de login fallido

Seguro:

logger.warning(f"Login fallido para usuario {username}")

Mishandling of Exceptional Conditions (A10)

  • Maneja errores con mensajes genéricos hacia el usuario, pero detallados en logs. 
  • Evita exponer stack traces en producción. 
  • Diseña flujos de fallback seguros (ejemplo: si falla autenticación externa, no dar acceso por defecto).

Inseguro:

# Mostrar error completo al usuario
except Exception as e:
    return str(e)

Seguro:

except Exception as e:
    logger.error(f"Error interno: {e}")
    return "Ha ocurrido un error, inténtalo más tarde."

La tabla del OWASP Top Ten es como nuestro mapa de riesgos más comunes. Si la convertimos en criterios de aceptación en nuestros proyectos, reducimos drásticamente la superficie de ataque.

Enlaces:

https://codemonkeyjunior.blogspot.com/2025/08/owasp-el-top-ten-de-las.html
https://owasp.org/Top10/2025/

LINQ: consultando documentos JSON

En el post anterior vimos cómo realizar consultas en archivos XML. Ahora veremos cómo consultar documentos JSON con LINQ.

Teniendo el siguiente documento JSON:

developer.json

{
  "desarrolladores":[
    {
	"id_empleado":"7894311",
	"nombre": "Karla Alvarez Montoya",
	"telefono": "555 32 53 138",
	"correo":"karla.alvarez@edu.mx"
	},
	{
	"id_empleado":"74533267",
	"nombre": "Laura Torres Jimenez",
	"telefono": "552 99 00 112",
	"correo":"laura.torres@edu.mx"
	},
	{
	"id_empleado":"55533800",
	"nombre": "Joan Sebastian Laredo Arsate",
	"telefono": "553 56 33 008",
	"correo":"joan.laredo.arsate@edu.mx"
	}
  ]
}

1. Vamos a crear un proyecto .NET para consultar ese documento.

$ dotnet new console -n DemoLinqJson
$ cd DemoLinqJson

2. Instalaremos los paquetes necesarios para manipular el docuemnto JSON:

Con la herramienta CLI:

$ dotnet add package Newtonsoft.Json --version 13.0.5-beta1

Con NuGet:

$ NuGet\Install-Package Newtonsoft.Json -Version 13.0.5-beta1

3. Modificamos el programa principal ``Program.cs``:

using System;
using System.Linq;
using Newtonsoft.Json.Linq;

class Program
{
    public static readonly string PATH = "developer.json";
    
    static void Main()
    {
       try{
        var json = File.ReadAllText(PATH);
        var data = JObject.Parse(json);
        Console.WriteLine("Contenido del archivo: {0}",data.ToString());
       } catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }
    }

}

4. Ejecutamos el proyecto:

$ dotnet run

Salida:

Contenido del archivo: {
  "desarrolladores": [
    {
      "id_empleado": "7894311",
      "nombre": "Karla Alvarez Montoya",
      "telefono": "555 32 53 138",
      "correo": "karla.alvarez@edu.mx"
    },
    {
      "id_empleado": "74533267",
      "nombre": "Laura Torres Jimenez",
      "telefono": "552 99 00 112",
      "correo": "laura.torres@edu.mx"
    },
    {
      "id_empleado": "55533800",
      "nombre": "Joan Sebastian Laredo Arsate",
      "telefono": "553 56 33 008",
      "correo": "joan.laredo.arsate@edu.mx"
    }
  ]
}

Como se puede observar hemos mostrado todo el contenido del archivo JSON. ¿Se podrá realizar consultas? Estamos a punto de averiguarlo.

Lo primero a hacer será crear una clase que contenga la lógica del JSON al leer.

Desarrollador.cs

using System.Text.Json.Serialization;

namespace model
{
    public class Desarrollador
    {
        [JsonPropertyName("id_empleado")]
        public string Id { get; set; } = string.Empty;
        [JsonPropertyName("nombre")]
        public string Nombre { get; set; } = string.Empty;
        [JsonPropertyName("telefono")]
        public string Telefono { get; set; } = string.Empty;
        [JsonPropertyName("correo")]
        public string Correo { get; set; } = string.Empty;
    }

    public class Root
    {
        [JsonPropertyName("desarrolladores")]
        public List<Desarrollador> Desarrolladores { get; set; }
    }
}

Como se puede ver, esta clase es coherente con los atributos del JSON. No solo por el nombre sino por el tipo de dato.

Ahora modifiquemos el programa principal.

Program.cs

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Text.Json;
using model;

class Program
{
    public static readonly string PATH = "developer.json";
    
    static void Main()
    {
        try
        {
            var json = File.ReadAllText(PATH);
            Root data = JsonSerializer.Deserialize<Root>(json);

            Console.WriteLine("Contenido del archivo:");
            foreach (var dev in data.Desarrolladores)
            {
                Console.WriteLine($"{dev.Id} - {dev.Nombre} - {dev.Correo}");
            }

            var desarrolladoresAgrupados = from dev in data.Desarrolladores
                                           group dev by dev.Id into idGroup
                                           select new
                                           {
                                               MyId = idGroup.Key,
                                               Count = idGroup.Count()
                                           };

            foreach (var group in desarrolladoresAgrupados)
            {
                Console.WriteLine($"Id del empleado: {group.MyId}, Count: {group.Count}");
            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }
    }
}

La parte que nos interesa es esta:

            var json = File.ReadAllText(PATH);
            Root data = JsonSerializer.Deserialize<Root>(json);

        
            var desarrolladoresAgrupados = from dev in data.Desarrolladores
                                           group dev by dev.Id into idGroup
                                           select new
                                           {
                                               MyId = idGroup.Key,
                                               Count = idGroup.Count()
                                           };

            foreach (var group in desarrolladoresAgrupados)
            {
                Console.WriteLine($"Id del empleado: {group.MyId}, Count: {group.Count}");
            }

Este bloque lee el contenido del archivo, lo deserializa y posteriormente agrupa a los desarrolladores por su Id y cuenta cuántos hay en cada grupo. Si nos recuerda a una consulta SQL es por que toma referencia a ese lenguaje.

Traducida a una consulta SQL vendría siendo algo como esto:

SELECT 
    id_empleado AS MyId,
    COUNT(*) AS Count
FROM desarrolladores
GROUP BY id_empleado;

Hacer consultas similares a los que se haría en SQL en archivos XML y/o JSON es posible gracias a LINQ.

Hay proyectos en los que nos será de mucha ayuda.

Continuaremos en próximas entregas.

Enlaces:

https://www.newtonsoft.com/json/help/html/queryinglinqtojson.htm
https://codemonkeyjunior.blogspot.com/2025/12/linq-consultando-documentos-xml.html
https://medium.com/@ddemirugur/c-13-net-9-part-10-linq-linq-to-xml-json-etc-f6f939f97222
https://developer.mescius.com/blogs/how-to-query-json-in-c-sharp-using-sql-linq-ado-net
https://learn.microsoft.com/es-es/dotnet/standard/linq/
https://www.johnpapa.net/linq-to-json-ndash-checking-for-an-object-or-array/
https://csharphardcoreprogramming.wordpress.com/2016/01/19/json-to-linq-to-json/
https://medium.com/hacking-building/using-linq-to-serialize-a-c-object-into-json-9ecbe9ca2941
https://codesignal.com/learn/courses/parsing-json-with-csharp/lessons/introduction-to-json-and-its-usage-in-csharp

LINQ: operaciones comunes

Continuamos con esta serie sobre LINQ y la lectura de archivos XML y JSON . Ahora profundizaremos un poco con las operaciones básicas ...

Etiquetas

Archivo del blog