Audito las capas de la plataforma que forman parte del alcance acordado.

He visto caídas, brechas y recuperaciones fallidas cuyo origen no estaba en una sola capa. Examino las relaciones entre el código, los datos, la arquitectura y la infraestructura, sin separar el rendimiento, la seguridad, la accesibilidad y la continuidad.

Criterio profesional

Una auditoría vale por lo que se puede comprobar después, no por lo gruesa que sea.

He recibido informes de auditoría de doscientas páginas que no permitían tomar ni una decisión. Estas son las condiciones que me exijo.

Cada hallazgo, con evidencia localizable

Cuando está en el código, indico el fichero, la línea y el fragmento exacto; en configuración, ejecución u operación, cito la clave, la traza, la métrica, el intervalo y los pasos necesarios para reproducirlo. Cuando se trata de una ausencia, documento el alcance completo revisado.

Severidad por consecuencia, no por gusto

Lo que expone datos o tumba el servicio va primero. Lo que molesta al leerlo va al final. Mezclar las dos cosas en la misma lista es la forma más rápida de que no se arregle ninguna.

Una regla produce una observación reproducible

La revisión determina si esa observación constituye un hallazgo. Separo los datos comprobables de los juicios técnicos y documento ambos de forma distinta.

Verificar antes de acusar

Un hallazgo plausible que resulta ser falso destruye la credibilidad de los otros veinte. Antes de escribir nada, intento demostrar que me equivoco; si no lo consigo, entonces es un hallazgo.

Dónde se concentra la complejidad

Se pide una auditoría cuando hay que decidir algo y falta la información para decidirlo.

Aceptar una entrega, heredar código sin documentación, comprar un producto, entender un incidente o averiguar qué impide actualizar. La pregunta de fondo se repite: qué hay realmente ahí dentro.

01

Hay que aceptar la entrega de un proveedor

El desarrollo llega terminado y alguien tiene que decir si se acepta. Sin criterio técnico, la conversación se reduce a si la pantalla se ve bien.

02

La organización hereda código sin documentación

Cambia el proveedor o se va la persona que lo mantenía. Falta documentación sobre lo que hace el código, sus dependencias y los riesgos de modificarlo.

03

Antes de comprar o de fusionar

Se va a adquirir un producto o a integrar un equipo, y la decisión depende de qué deuda técnica se compra con él y cuánto costará mantenerla.

04

Después de un incidente de seguridad

Ha ocurrido algo y hace falta saber si fue un caso aislado o el síntoma de una forma de programar que va a repetirlo.

05

La plataforma no se puede actualizar

Cada versión nueva exige un proyecto. Hay que saber qué desarrollos concretos lo impiden y qué costaría desatascarlos.

06

Se va a publicar algo hacia fuera

Antes de distribuir una extensión o abrir un servicio, conviene que alguien lo revise con la mirada de quien intentará romperlo.

Qué miro y qué queda por escrito

Dimensiones de la auditoría y evidencia que documento.

Audito código, datos, configuración e infraestructura según el alcance acordado. La revisión de código es una de esas dimensiones, no necesariamente el encargo completo.

Auditoría de seguridad del código

Permisos, validación de entrada, escapado de salida, consultas, control de acceso y tratamiento de datos personales. Cada hallazgo llega con la línea exacta y con lo que un atacante conseguiría.

  • Permisos y control de acceso
  • Validación y escapado
  • Inyección en consultas
  • Datos personales
  • Prueba de explotación

Auditoría de calidad y mantenibilidad

Estructura, duplicación, acoplamiento, cumplimiento de las convenciones de la plataforma y decisiones que pueden dificultar una actualización. Estos factores condicionan el coste de mantenimiento durante el horizonte acordado.

  • Estructura y acoplamiento
  • Duplicación
  • Convenciones de la plataforma
  • Deuda con fecha
  • Coste de mantenimiento

Auditoría de la cobertura de pruebas

Si las pruebas fallan cuando deben, si cubren el recorrido real y no solo el recorrido previsto, y qué casos quedan sin comprobar. La ausencia se verifica sobre toda la batería, no fichero a fichero.

  • Efectividad real
  • Cobertura funcional
  • Casos sin cubrir
  • Pruebas que dan falsa seguridad

Auditoría de rendimiento y operación

Consultas, índices, caché, procesos en segundo plano y comportamiento con volumen. Además, qué se ve cuando algo falla y si se puede revertir un despliegue.

  • Consultas e índices
  • Caché y procesos en cola
  • Comportamiento con carga
  • Observabilidad
  • Capacidad de revertir

Aceptación de entregas de proveedor

Revisión con arreglo a criterios acordados antes de aceptar el trabajo, con un veredicto que la organización puede defender en una conversación contractual.

  • Criterios acordados
  • Veredicto sostenible
  • Hallazgos rebatibles
  • Seguimiento de correcciones

Revisión continua, no solo puntual

He montado las mismas reglas para que se ejecuten en cada cambio, de forma que el equipo recibe el hallazgo cuando aún es barato corregirlo y no dos años después en un informe.

  • Reglas en cada cambio
  • Umbral de aceptación
  • Histórico de deuda
  • Criterio compartido con el equipo

Responsabilidades concretas

Respondo de que cada hallazgo se pueda comprobar, discutir y arreglar.

Un informe que nadie puede verificar no cambia nada. Cada hallazgo lleva su cita, su gravedad razonada y la decisión que abre, para que la organización pueda actuar o aceptar el riesgo a sabiendas.

01

Definir el alcance antes de empezar

Qué se audita, con qué profundidad y contra qué criterios. Una auditoría sin alcance escrito acaba siendo interminable o superficial, y a veces las dos cosas.

02

Buscar lo que expone o rompe

Permisos que no se comprueban, entradas que no se validan, consultas construidas por concatenación, datos personales que salen donde no deben y control de acceso que se apoya en que nadie mire la dirección.

03

Medir si las pruebas prueban algo

No cuento cuántas pruebas hay: compruebo si fallan cuando el código se rompe. Una batería que pasa haga lo que haga el programa da una seguridad falsa.

04

Estimar el coste de mantener

Duplicación, dependencias sin gobierno, acoplamiento con detalles internos de la plataforma y todo lo que convierta la próxima actualización en un proyecto.

05

Comprobar el comportamiento, no solo el código

Lo que se lee bien puede caerse con volumen. Reviso consultas, índices, caché y lo que ocurre en el momento de más uso, que es cuando importa.

06

Entregar decisiones, no una lista

Cuando es posible estimarlo, cada hallazgo incluye un rango para corregirlo y para asumir el riesgo, con sus supuestos e incertidumbre. La organización conserva la decisión.

Mapa de especialización

Lo que miro cuando una plataforma no aguanta lo que le pide su calendario.

Cuando reviso el rendimiento de una plataforma no empiezo por la configuración: busco qué operación genera la demanda y qué recurso deja de atenderla. Estos son los frentes que miro y los términos con los que trabajo en cada uno.

En Moodle, el promedio suele ocultar justo el momento que rompe la plataforma.

He encontrado caídas que no nacían de una única configuración incorrecta, sino de la coincidencia entre una ráfaga, una cola, un bloqueo o una dependencia externa. Por eso no busco una receta universal: reconstruyo qué operación generó la demanda y qué recurso dejó de atenderla.

  • Avalanchas a una hora exacta
  • PHP-FPM agotado con CPU disponible
  • Sesiones y bloqueos en el cuello de botella equivocado
  • Redis utilizado como una caja única
  • cron y task_adhoc acumulados
  • Consultas y tablas que crecen con el uso

Moodle concentra estado, tareas y reglas académicas que no aparecen en una aplicación web genérica.

No reduzco Moodle a PHP, base de datos y nodos web. Sigo el recorrido hasta el motor de preguntas, las calificaciones, las tareas planificadas, los eventos, las extensiones y `moodledata`.

  • Cuestionarios y motor de preguntas
  • MUC, sesiones y bloqueos
  • Tareas planificadas y ad hoc
  • Calificaciones y recalculados
  • Eventos y registro de actividad
  • moodledata

Dimensiono por operaciones y tiempos de servicio, no por usuarios matriculados.

Construyo el modelo de demanda con llegadas, concurrencia efectiva, throughput y duración de cada operación. Cuando procede, utilizo la ley de Little para comprobar si las cifras de concurrencia, tasa y tiempo de respuesta son coherentes.

  • Escenarios que comparo
  • Matriz de capacidad
  • Escalar o reducir demanda
  • Coste por carga útil
  • Margen y tiempo de reacción

Una alerta de CPU no explica por qué un alumno no puede entregar un examen.

Organizo la observabilidad alrededor de operaciones Moodle y de recursos concretos. Utilizo métricas RED —tasa, errores y duración— para los recorridos y USE —utilización, saturación y errores— para la infraestructura.

  • Paneles por recorrido y por capa
  • Alertas basadas en síntomas
  • Registros y trazas utilizables
  • Procedimientos de respuesta
  • Análisis posterior

Una dependencia lenta puede agotar Moodle aunque Moodle no sea la causa.

Sigo las llamadas a SAML, OAuth 2/OIDC, LDAP, sistemas académicos, ERP, HRIS, CRM, proctoring, videoconferencia, correo, antivirus y repositorios. Mido cuánto presupuesto de latencia consume cada una y qué ocurre cuando responde tarde o deja de responder.

  • Identidad y aprovisionamiento
  • Tiempos límite y reintentos
  • Sistemas académicos y corporativos
  • SCORM, xAPI y LRS
  • Trabajo síncrono y asíncrono

Separo la actividad académica de las consultas que intentan explicarla.

Evito que informes, exportaciones y cuadros de mando compitan con cuestionarios, entregas y calificaciones. Analizo qué datos necesitan inmediatez y cuáles pueden trasladarse mediante ETL o ELT a un almacén analítico.

  • Carga transaccional y carga analítica
  • Calidad y linaje
  • Retención y minimización
  • Pseudonimización y anonimización
  • Derechos sobre los datos

Un hallazgo técnico termina cuando otra persona puede comprobarlo.

Documento el síntoma, la evidencia, las hipótesis descartadas, la causa demostrada, el impacto y la prueba que confirma la corrección. Si una afirmación depende de una métrica, conservo el intervalo, la zona horaria, la consulta, el filtro y la versión de la configuración utilizada.

  • Un hallazgo de rendimiento
  • Un plan de carga
  • Un plan de ejecución
  • Una matriz de capacidad
  • Un procedimiento operativo
  • Una regla versionada

Casos de uso

Casos de auditoría: qué parecía, qué era y cómo lo comprobé

Cada uno cuenta lo mismo: qué medí, qué parecía al principio, por qué lo descarté y qué era en realidad. Van anonimizados por tipo de organización, con las cifras que se midieron.

Caso

Miles de procesos regeneraban a la vez los mismos objetos de caché

Durante una prueba de carga, el clúster caía en el primer minuto y medio y devolvía errores 500 y 502. Medí la latencia en el balanceador, las conexiones activas a Redis y la E/S de disco en PostgreSQL. Al principio pensé en falta de escalado en la base de datos o en los nodos de PHP-FPM, hasta que vi que la saturación coincidía con miles de consultas idénticas y con el agotamiento de conexiones a Redis. Era una estampida de regeneración: miles de claves de la caché de aplicación expiraban a la vez y cientos de procesos PHP reconstruían los mismos objetos de contexto y de plugins. Separé las sesiones y la caché de aplicación en almacenes de Redis independientes, mantuve la caché de petición dentro de cada proceso, escaloné las caducidades y añadí un bloqueo por clave con actualización en segundo plano: mientras un proceso regenera, los demás reciben temporalmente la copia caducada. Activé además conexiones persistentes con serialización binaria para evitar una nueva conexión TCP en cada petición. En el recorrido y la duración documentados en el informe confidencial, la plataforma sostuvo 15.000 sesiones concurrentes. Las cifras de latencia y uso de CPU pertenecen a ese escenario y no constituyen una capacidad general de la plataforma.

    Caso

    El autoguardado concentraba las escrituras de 25.000 cuestionarios en el mismo minuto

    En una convocatoria con decenas de miles de cuestionarios a la vez, los intentos y las respuestas se bloqueaban, los interbloqueos se repetían y el rendimiento se desplomaba a mitad de examen. Medí las consultas lentas, los hilos en espera de bloqueo, la memoria de la base de datos y la frecuencia de autoguardado. Lo primero que me pidieron fue una instancia con más CPU y memoria. Los datos decían otra cosa: la carga se concentraba en escrituras síncronas periódicas y en la competencia entre lecturas y escrituras sobre el mismo nodo. El origen era el autoguardado de respuestas, que se ejecutaba cada minuto mientras las consultas de intentos y de navegación seguían leyendo del nodo de escritura. Ajusté el intervalo de autoguardado y las peticiones de finalización. Desvié a réplicas únicamente las lecturas que toleraban retraso y mantuve en el nodo principal las que debían reflejar de inmediato una escritura. También afiné max_connections, shared_buffers, work_mem y el autovacuum. En la repetición documentada desaparecieron los interbloqueos y se alcanzó la concurrencia prevista; las métricas detalladas pertenecen al informe confidencial del caso.

      Caso

      Una consulta sin índice recorría doce millones de filas cada vez que se mostraba un bloque

      Después de una migración de versión, abrir la página principal de un curso pasó de 400 ms a más de 6 segundos bajo carga, en una plataforma con años de extensiones acumuladas. Perfilé las peticiones con instrumentación de PHP, conté las consultas de cada una y medí su duración. Sospeché primero de la memoria de los nodos PHP y de la acumulación histórica en la base de datos. El perfilado lo desmintió al aislar una sola consulta cuyo plan recorría la tabla entera. Venía de una tabla propia de un plugin de seguimiento: doce millones de filas y ningún índice compuesto por curso y usuario. Activé el registro de consultas lentas, analicé el plan de ejecución, creé el índice que faltaba y reescribí el código para sustituir las consultas dentro del bucle por una sola consulta agregada. El tiempo bajó de 6 s a 250 ms y las lecturas de disco se redujeron un 85 %.

        Caso

        Los informes expulsaban de memoria los datos operativos al recorrer más de ochenta millones de registros

        En un Centro de Formación Profesional, la plataforma se congelaba cada mañana cuando los coordinadores generaban los informes de seguimiento y los estudiantes se quedaban colgados. Medí las consultas lentas, los aciertos de caché de consultas y los bloqueos de metadatos. Parecía que el servidor se quedaba sin procesos PHP libres por un pico imprevisto de alumnos, y lo descarté porque los bloqueos coincidían con consultas por fecha y contexto sobre el registro de actividad. La tabla tenía más de ochenta millones de filas y no estaba particionada. Las consultas de los tutores ocupaban memoria de la base de datos y expulsaban del área de trabajo las páginas que sí utilizaba la actividad diaria. Separé los registros hacia un almacén de solo lectura, dejé de guardar eventos que nadie consultaba, particioné por rangos de fecha y programé el archivado de lo que tuviera más de doce meses. Se acabaron las congelaciones durante los informes y la base operativa se redujo más de un 60 %.

          Caso

          Borrar un 40 % de los datos no redujo las páginas que la base de datos seguía leyendo

          En una institución educativa, la base de datos respondía más despacio después de purgar 50.000 usuarios inactivos y reiniciar 3.000 cursos, aunque contenía un 40 % menos de datos. Medí el tamaño físico en disco, la fragmentación de los índices y las lecturas de página por consulta. Parecía corrupción de la base de datos o un fallo del almacenamiento, y lo descarté porque los ficheros conservaban prácticamente el mismo tamaño y las consultas seguían recorriendo páginas vacías. Los borrados masivos habían dejado huecos en las páginas de tablas e índices, pero el motor mantenía ese espacio fragmentado. Escribí una comprobación para localizar las tablas con más de un 30 % de espacio libre, reconstruí tablas e índices durante una ventana de mantenimiento y ajusté el factor de llenado para las tablas con muchas lecturas y pocas actualizaciones. Recuperé más de 150 GB de disco y reduje un 35 % el tiempo de las consultas que leían por índice.

            Caso

            El servicio confundía autenticación con autorización y exponía los datos de todo el alumnado

            Durante una revisión de seguridad detecté que cualquier usuario autenticado podía leer datos personales y calificaciones de otros a través de un servicio propio. Lo confirmé combinando el análisis estático del control de acceso con pruebas dinámicas sobre ese servicio. Parecía que la autenticación bastaba para proteger el acceso, y descarté esa idea: estar autenticado no es estar autorizado. El servicio consultaba la base de datos sin comprobar la capacidad correspondiente ni validar el contexto. Lo reescribí con la API oficial de servicios externos de Moodle, forcé la validación del identificador contra el contexto del curso y del sistema, y limité la respuesta a los atributos necesarios. Añadí también reglas en el cortafuegos de aplicación para detectar recorridos secuenciales de identificadores. El vector quedó cerrado y la reevaluación superó la prueba de intrusión.

              Caso

              La conformidad exigía corregir barreras que el escaneo automático no detectaba

              La plataforma tenía que cumplir los requisitos de accesibilidad definidos para el encargo y la revisión inicial encontró barreras. Combiné el escaneo automático con una revisión manual, con lectores de pantalla y navegando solo con el teclado. Así vi que los cuestionarios atrapaban el foco, el tema corporativo no llegaba al contraste mínimo y varios componentes de terceros no funcionaban con lectores de pantalla. Sobre el papel podía parecer suficiente cumplir WCAG 2.1 en nivel AA, pero la norma europea EN 301 549, en su versión armonizada 3.2.1, añade requisitos propios. Es el marco que exige en España el Real Decreto 1112/2018. El uso real destapó problemas que el escaneo no encontraba. Reescribí el CSS y las plantillas para corregir el contraste, añadí los atributos que anuncian los cambios de estado en menús, acordeones y ventanas modales, y recuperé el indicador de foco que alguien había suprimido. Resolví además la trampa de foco de las preguntas de arrastrar y soltar con una alternativa por teclado. En la versión, la muestra y los criterios evaluados, la revisión final no dejó hallazgos abiertos en la interfaz base ni en los tipos de cuestionario incluidos.

                Caso

                El ataque entraba saneado como HTML y se ejecutaba después desde una tarea programada

                Un escaneo rutinario del cortafuegos de aplicación detectó cargas sospechosas dirigidas a las tareas en segundo plano de un desarrollo propio. Parecía un intento convencional, ya neutralizado al sanear el formulario. Lo descarté siguiendo el recorrido de los datos no fiables y contrastándolo con el registro general de consultas: un usuario sin autenticar podía acabar ejecutando código en producción. Era una inyección de SQL de segundo orden. Los fragmentos entraban por campos de texto, se guardaban saneados como HTML y más tarde el plugin los concatenaba sin parametrizar en una consulta que movía datos entre tablas. Desde ahí se llegaba al sistema de ficheros con sentencias múltiples o funciones privilegiadas. Sustituí las consultas concatenadas por la API de base de datos de Moodle con parámetros con nombre y añadí a la integración continua una regla que rechazaba cualquier concatenación de variables en llamadas a la base de datos. Eliminé el vector y la auditoría externa terminó sin hallazgos críticos.

                  Caso

                  El estado de una encuesta permitía encadenar clases hasta ejecutar comandos

                  Durante una auditoría interna detecté que alguien con rol de profesor podía ejecutar comandos en el sistema operativo a través de una extensión sin mantenimiento. Parecía un problema de permisos del rol, pero el análisis estático del código destapó una deserialización insegura. La extensión recibía una cadena preparada en un campo del formulario o en una cookie de sesión y la deserializaba directamente, lo que permitía encadenar clases ya cargadas en memoria hasta ejecutar código. Sustituí la serialización nativa de objetos por JSON, con lo que la ejecución dejó de ser posible. Donde los registros antiguos impedían migrar de inmediato, restringí la deserialización bloqueando la instanciación de clases. Eliminé el vector y la extensión quedó conforme al estándar de desarrollo seguro de la plataforma.

                    Cómo trabajo

                    Primero lo que se comprueba solo, después lo que solo ve una persona con oficio.

                    Las reglas deterministas producen observaciones reproducibles; la lectura experta decide cuáles constituyen un problema en este contexto. Antes de escribir un hallazgo, intento demostrar que me equivoco.

                    1. 01

                      Acordar el alcance

                      Qué entra, qué queda fuera, con qué criterios se juzga y qué decisión debe permitir tomar el informe.

                    2. 02

                      Pasar las reglas

                      Comprobaciones deterministas sobre la estructura del código. Con la misma versión, configuración, entorno y datos de entrada, producen un resultado reproducible y no dependen de quién pulse el botón.

                    3. 03

                      Leer lo que ninguna regla ve

                      La revisión experta encuentra problemas de diseño y de contexto que las reglas no expresan. Sus resultados se combinan con las comprobaciones automáticas y la evidencia de ejecución.

                    4. 04

                      Intentar tumbar cada hallazgo

                      Antes de escribirlo, busco la explicación que lo invalide. Lo que sobrevive a ese intento es lo que entra en el informe.

                    5. 05

                      Ordenar por consecuencia

                      Cada hallazgo con su gravedad, su cita, lo que cuesta arreglarlo y lo que cuesta dejarlo. La organización decide con esa información delante.

                    Experiencia y trabajo propio

                    Reviso el tipo de código que llevo veinte años escribiendo, y esa es la diferencia.

                    Ver trayectoria y perfil
                    • Más de veinte años escribiendo el tipo de código que ahora reviso, que es lo que permite distinguir un riesgo real de una preferencia de estilo.
                    • Motor propio de reglas que analiza la estructura del código y detecta problemas de seguridad, de uso indebido de las interfaces de la plataforma y de convenciones.
                    • Método de revisión por dimensiones separadas, con una pasada adversarial que intenta refutar cada hallazgo antes de darlo por bueno.
                    • Auditoría de la efectividad de las pruebas, no solo de su existencia: si no fallan cuando el código se rompe, no cuentan.
                    • Informes con cita exacta, severidad razonada y la decisión que corresponde a cada hallazgo.

                    Blog

                    En el blog cuento qué busco al revisar código y por qué.

                    Escribo sobre los hallazgos que se repiten en casi todas las auditorías, sobre las pruebas que dan seguridad falsa y sobre cómo se defiende un veredicto técnico delante de un proveedor que no lo comparte.

                    Ir a blog.albertolarah.com

                    Hablamos

                    Si hay que decidir sobre un software y falta información, hablamos

                    Aceptar una entrega, heredar un desarrollo o entender por qué una actualización lleva bloqueada meses. Si estás ante una de esas decisiones, escríbeme.