Tarjeta de título de «Revisión de código de seguridad explicada» que muestra un panel de diferencias de código con una línea original y una línea modificada, y tres etapas etiquetadas debajo: PATRONES (determinista), luego APROBADO, luego RAZONAMIENTO (modelo).
Guides & Insights

Revisión de código de seguridad explicada: qué busca un revisor en un diff y por qué

Autor

Rowan Sterling

Fecha de publicación

Últimos modelos · 20Ver todos los modelos →
Benchmarks: Artificial Analysis · actualizado a diario
Volver a todas las publicaciones

Una revisión de seguridad del código es la lectura deliberada del código fuente con el propósito específico de encontrar las formas en que se puede hacer que haga algo que su autor no pretendía. Esa definición suena obvia hasta que se compara con lo que la mayoría de los equipos realmente ejecutan, que es una revisión funcional con una lista de verificación de seguridad cerca. La OWASP Secure Code Review Cheat Sheet traza la línea con precisión: una revisión de seguridad se diferencia de una revisión funcional en que se centra en la validación de entradas, la autenticación, la autorización, la implementación criptográfica y los vectores de ataque, en lugar de en la corrección. Un diff que funciona perfectamente puede aun así abrir a un atacante un camino hacia el sistema, y solo una de esas dos revisiones lo está buscando.

Lo que hace que el tema merezca un artículo ahora en lugar de en 2020 es que un modelo de lenguaje puede leer un diff de forma competente, económica y continua —lo que cambia la economía de la revisión sin cambiar ni una sola de las dimensiones que se revisan. La lista de comprobación es anterior a los modelos y no ha cambiado. Lo que ha cambiado es la frecuencia con la que puedes permitirte ejecutarla.

Lo que un revisor realmente busca al leer

La hoja de referencia de OWASP organiza el trabajo en seis áreas, y lo útil de la lista es que cada área es una pregunta en lugar de una categoría. Las categorías invitan a marcar casillas; las preguntas producen hallazgos.

Card 'What a reviewer is actually looking for' listing six dimensions - input and injection, authn and session, authorization, cryptography, business logic, configuration and secrets - each with the question it answers about a diff.

Entrada e inyección. La cuestión no es si el código tiene un saneador; es si algo llega a un intérprete —shell, SQL, motor de plantillas, evaluador de expresiones— y en qué capa se supone que debe aplicarse ese requisito. Un diff que añade una consulta parametrizada es bueno; un diff que añade una consulta parametrizada a una función cuyos otros llamadores pasan cadenas sin procesar es un diff que ha movido el problema.

Autenticación y sesión. ¿Quién es el llamante y cómo estableció el código esa identidad? El error recurrente no es una comprobación de contraseña defectuosa, sino una sesión que sobrevive a un cambio de privilegios: se produce la elevación, el token conserva sus claims antiguos y, una hora después, los privilegios siguen ahí.

Autorización. Comprobaciones a nivel de objeto, siempre, en cada ruta. Lo concreto que hay que buscar en un diff es una ruta nueva que no herede el guard que tienen sus rutas hermanas, porque el middleware se registra por router y una ruta añadida bajo otro distinto simplemente no quedará cubierta.

Criptografía. Primitiva, modo, longitud de clave y dónde reside la clave. Dos preguntas detectan la mayoría de los hallazgos reales: ¿puede cualquiera que pueda leer este código fuente leer también la clave, y es esta una comparación que no debe filtrar información por tiempos?

Lógica de negocio. Si la secuencia puede reordenarse, reproducirse o regirse por valores que el cliente eligió. Un precio, una cantidad, un rol o una transición de estado que llega desde el cliente y no se vuelve a derivar en el servidor constituye un hallazgo, independientemente de cómo se valide.

Configuración y secretos. Lo que el diff incluye y que nunca debería haber estado en un repositorio, y si los valores predeterminados son seguros allí donde el código se ejecuta realmente. Un indicador de depuración que viene activado por defecto es un hallazgo sobre producción, no sobre el indicador.

Revisión de línea base versus revisión de diferencias

OWASP separa dos modos, y confundirlos es una fuente común tanto de esfuerzo desperdiciado como de falsa confianza. Una revisión de línea base examina exhaustivamente el código base y es la herramienta adecuada para una aplicación nueva o una versión principal, para incorporar un sistema heredado, para cumplir un requisito de cumplimiento o para realizar el análisis posterior al incidente después de que algo ya haya salido mal. Una revisión basada en diferencias solo examina lo que cambió y es la herramienta adecuada para pull requests, commits, la finalización de funcionalidades y la validación continua.

La distinción importa porque ambas producen un trabajo diferente. Una revisión de línea base termina con una lista de pendientes; una revisión de diferencias termina con una decisión de fusión. Ejecutar la primera de forma continua es inviable económicamente, y ejecutar la segunda sobre una base de código que nunca ha tenido la primera es la forma en que los equipos acumulan una falsa sensación de cobertura: cada cambio pasa la revisión y la concatenación de SQL preexistente sigue ahí, intacta.

Screenshot of the OWASP Cheat Sheet Series index entry for Secure Code Review, showing the introduction describing manual examination of source code for vulnerabilities automated tools miss, the baseline-versus-diff review split with their use cases, and a table of contents covering methodology, patterns, techniques, checklists and tools.

Técnicas y qué detecta cada una

OWASP enumera cuatro técnicas de revisión, y se distinguen por la evidencia que sigue el revisor, no por lo exhaustivas que sean. El análisis de patrones de código sigue formas conocidas: la llamada a la función peligrosa, la deserialización de entrada no confiable, la comprobación de longitud ausente. Es rápido y es exactamente lo que una herramienta determinista hace bien. El análisis de flujo de datos sigue un valor desde donde entra hasta donde se usa, que es la única forma de responder si el sanitizador aplicado tres funciones atrás sigue aplicándose aquí. La revisión basada en amenazas parte de la capacidad de un atacante y avanza hacia delante, que es cómo se detecta la brecha entre dos componentes individualmente correctos. La revisión de lógica de negocio lee el flujo de trabajo en lugar de las líneas, preguntando qué secuencia de operaciones legítimas produce un estado ilegítimo.

Solo el primero de esos cuatro se beneficia realmente de un escáner, y solo el primero y el segundo se benefician de un pase de modelo sin guía. Eso no es un argumento contra los modelos; es un argumento a favor de indicarle al revisor qué técnica se desea, porque un prompt que pide «problemas de seguridad» producirá de forma fiable un análisis de patrones y se detendrá ahí.

Lo que la IA cambió y lo que no

La investigación sobre código escrito por IA agudizó el cuello de botella de la revisión en lugar de crearlo. El estudio de NYU sobre las sugerencias de GitHub Copilot, publicado en IEEE S&P en 2022, encontró que aproximadamente el 40 % de los fragmentos generados contenía un patrón explotable en 18 categorías de CWE. El trabajo de Stanford sobre el comportamiento de los desarrolladores, publicado en ACM CCS en 2023, encontró el efecto más trascendente: los desarrolladores que usaban asistentes tenían más confianza en que su código era seguro mientras producían código mediblemente menos seguro. Más producción y más confianza constituyen un problema de revisión.

Un modelo de lenguaje como revisor cambia una variable en esa ecuación: el rendimiento. Puede leer cada solicitud de extracción, con un costo por token en lugar de por asiento, y se le pueden plantear explícitamente las seis preguntas anteriores. No cambia las preguntas y no adquiere las dos propiedades que hacen que una revisión sea confiable: el determinismo y la capacidad de ejecutar el código. El mismo diff revisado dos veces puede producir hallazgos diferentes, las severidades varían entre ejecuciones y el revisor no puede demostrar que una ruta señalada sea alcanzable.

Screenshot of the OrcaCode Review product page on OrcaRouter headed 'Open engine. Open prompt. Open bill.', describing a pull-request reviewer whose rules and merge gate run on GitHub under an MIT licence and are billed per token, with an example finding about an idempotency key that still varies in time.

OrcaCode Review es la forma productizada de esa idea, y merece la pena examinarla de cerca porque las decisiones de diseño revelan los modos de fallo. Una revisión que aísla el caso fallido como un comentario concreto y reproducible —esta clave aún varía en el tiempo, así que un cargo reintentado genera una nueva clave y puede cobrar dos veces, aquí está el cambio de una línea— está haciendo el trabajo que hace un revisor. Una revisión que informa de un «posible problema de seguridad» sin una reproducción está haciendo análisis de patrones con peor precisión que el emparejador de patrones. Y un motor de reglas y compuertas publicado bajo una licencia abierta está haciendo una afirmación implícita que merece la pena comprobar: que el juicio del revisor también debería ser revisable.

El arreglo de dos capas que funciona

Dado un escáner determinista y un revisor probabilístico, la organización que resiste en la práctica es dar a cada uno el trabajo en el que realmente es bueno. El escáner se ejecuta en cada push: segundos, sin modelo, sin varianza, detectando secretos, llamadas a API peligrosas, deserialización insegura e importaciones alucinadas. El revisor se ejecuta en cada pull request: minutos, respaldado por un modelo, detectando la lógica entre líneas: si la comprobación de autorización se ejecuta antes de la obtención de datos, si la clave de idempotencia está vinculada a un valor estable, si la nueva ruta heredó la protección.

Luego viene la capa que la gente se salta: la barrera. Un hallazgo que no bloquea una fusión es una notificación, y las notificaciones se leen los viernes. La barrera tiene que ser estrecha y tiene que ser absoluta —los hallazgos críticos bloquean, todo lo demás se comenta— y la exención tiene que quedar por escrito, porque el valor de una barrera no está en que detenga el primer hallazgo, sino en que hace visible cada evasión.

Revisando el cambio, no la base de código

La disciplina práctica para una revisión de diff es una secuencia breve, y su valor es que se puede enseñar en una tarde. Lee el diff por lo que añade a la superficie de ataque en lugar de por lo que cambia. Para cada nuevo punto de entrada, sigue la entrada un salto más allá de donde termina el diff —la vulnerabilidad suele estar en la función a la que llama, que el autor no leyó. Para cada nueva ruta, comprueba el registro de la protección. Para cada nueva dependencia, comprueba si existe y si alguien más publicó el nombre. Para cada nuevo valor de configuración, pregunta a qué se asigna por defecto donde realmente se desplegará.

Nada de eso requiere conocer bien el lenguaje, por lo que escala a revisores que no son especialistas en seguridad. Sí requiere que el revisor vea el diff en lugar del archivo, porque todo el método depende de saber qué es nuevo.

Y el límite honesto: una revisión de diff no puede encontrar el fallo que ya estaba ahí antes del diff. Si la aplicación nunca ha tenido una revisión de referencia, el trabajo de seguridad de mayor valor que se puede hacer no es un mejor revisor — es una pasada deliberada por el código, en las áreas que tocan dinero, identidad y acceso a archivos, con las seis preguntas anteriores. Después de eso, cada revisión de diff posterior protege algo que ya se ha revisado una vez. Antes de eso, cada revisión de diff protege algo desconocido.