Audita tu Propio Código Como Si Fueras el Atacante
No puedes revisar tu propio código (y aun así tienes que hacerlo)
Cuando revisas tu propio trabajo, tu cerebro hace trampa. Lees lo que quisiste escribir, no lo que escribiste. Tu objetivo implícito es confirmar que está bien — y el sesgo de confirmación es muy bueno encontrando evidencia de lo que ya crees. Por eso la auto-revisión clásica caza erratas, nombres de variable y formato, pero se le escapan los fallos de diseño, los huecos de seguridad y los casos límite. Esos no se ven cuando buscas confirmación; se ven cuando buscas sangre.
La revisión adversarial es un cambio de objetivo deliberado: en lugar de “demostrar que esto funciona”, te propones demostrar que esto está roto. Es la misma diferencia que entre un test que pasa y un test que intenta romper. Y aunque suena a algo reservado a pentesters, es una disciplina que puedes —y deberías— aplicar a tu propio código antes de que lo haga alguien con peores intenciones.
La diferencia entre revisar y atacar
Revisar pregunta “¿está bien hecho?”. Atacar pregunta “¿cómo lo rompo?”. Son dos modos mentales distintos y producen hallazgos distintos.
El modo atacante asume hostilidad por defecto. La entrada no es la que esperas, es la peor posible. El usuario no es benévolo, es un adversario con tiempo. La red falla en el peor momento, dos peticiones llegan a la vez justo donde no toca, y el dato que “siempre” viene relleno llega nulo. No estás comprobando que el camino feliz funcione — estás cazando los caminos infelices que nadie escribió a propósito.
Antes de mirar una línea, cambia la pregunta que te haces. No “¿esto es correcto?” sino “si tuviera que provocar un fallo, una fuga o un dato corrupto desde aquí, ¿por dónde entraría?”. Ese reencuadre, por sí solo, cambia lo que tus ojos ven.
Una pasada por cada sombrero
El error más común al revisar es intentar verlo todo a la vez y, en la práctica, no ver casi nada. La revisión adversarial funciona mucho mejor por lentes: una pasada completa con un solo sombrero puesto, luego otra con el siguiente.
- Corrección — ¿hace lo que dice? ¿los casos límite, el vacío, el cero, el negativo?
- Seguridad — ¿entradas no confiables, inyección, autorización, secretos en logs?
- Concurrencia — ¿qué pasa si esto se ejecuta dos veces a la vez? ¿hay estado compartido sin proteger?
- Modos de fallo — ¿qué pasa cuando la dependencia de la que tira esto se cae? ¿el error se propaga o se traga en silencio?
- Rendimiento — ¿escala con el peor caso de tamaño de entrada, o es lineal donde debería ser constante?
Cada lente te hace una persona distinta durante diez minutos. La de concurrencia no se distrae con un nombre feo; la de seguridad no perdona un catch vacío. Mezclarlas es la receta para que todas pierdan agudeza.
No te fíes del primer veredicto
Un solo revisor —humano o agente— se equivoca en dos direcciones: ve fallos donde no los hay (falsos positivos) y se pierde fallos reales (falsos negativos). La forma de domar ambos es la verificación independiente: más de un revisor, sin que se vean entre sí, y una regla de decisión sobre sus votos.
- Para confirmar que un hallazgo es real, exige mayoría. Si tres revisores miran un supuesto bug y solo uno lo ve, probablemente es ruido. Esto recorta los falsos positivos que te harían perder el tiempo.
- Para descartar que algo es seguro, sé estricto: que un revisor levante la mano basta para investigar. Aquí el coste de un falso negativo (un bug real ignorado) es mayor que el de mirar de más.
La independencia es la clave, y es frágil. Si los revisores se ven los veredictos entre sí, se anclan y dejan de ser independientes: el segundo tiende a ratificar al primero. Cada juicio tiene que emitirse a ciegas sobre el estado del código, no sobre la opinión de otro revisor.
Clasifica por severidad
Un hallazgo sin severidad es ruido sin accionar. Antes de cerrar la auditoría, cada hallazgo recibe una clasificación —el esquema CVSS 3.0 es un buen estándar de partida— en cuatro niveles: Crítico, Alto, Medio, Bajo. La severidad no es burocracia: es lo que te dice qué arreglar antes de desplegar, qué arreglar este sprint y qué anotar como deuda consciente.
| Severidad | Significado | Acción |
|---|---|---|
| Crítico | Explotable, impacto grave, sin mitigación | Bloquea el despliegue |
| Alto | Riesgo serio, mitigación parcial | Arréglalo antes de cerrar |
| Medio | Riesgo real en ciertas condiciones | Planifícalo, no lo ignores |
| Bajo | Defensa en profundidad, higiene | Anótalo y vigílalo |
”Cero críticos” no es seguridad
Aquí está la trampa que hunde a más equipos: terminar una auditoría con cero hallazgos críticos y cantar victoria. La ausencia de críticos no es lo mismo que seguridad. Lo primero, porque un crítico que no encontraste no aparece en el informe — y el informe solo refleja lo que miraste. Lo segundo, porque los hallazgos medios y bajos se componen: tres “medios” alineados —un log que filtra de más, una validación floja y un permiso heredado— pueden encadenar el incidente que ningún “crítico” aislado habría provocado.
Un informe de auditoría con cero críticos describe lo que el auditor miró y no encontró, no lo que existe. Trátalo como un mapa parcial, no como un certificado. La pregunta útil no es “¿salió limpio?” sino “¿qué no llegamos a mirar?”. Lo que no auditaste se lee, equivocadamente, como auditado.
Agentes como red team
Hay una vuelta de tuerca moderna: usar agentes de IA como revisores adversariales. Encajan sorprendentemente bien, porque el patrón que ya describimos —varios revisores independientes con lentes distintas— se paraleliza de forma natural. Lanzas varios agentes a la vez, a cada uno le das una lente y la instrucción explícita de refutar (no de aprobar), y aplicas la regla de mayoría sobre sus veredictos.
La clave es el prompt adversarial: pídele al agente que asuma que el código es culpable y que su trabajo es probar la culpabilidad, no la inocencia. Un revisor —de silicio o de carbono— al que le pides que confirme que todo está bien, confirmará que todo está bien. Uno al que le pides que encuentre el fallo, busca de verdad. El sesgo de confirmación también aplica a las máquinas; el antídoto es el mismo: cambiar la pregunta.
Una checklist adversarial
Para una sesión concreta, esta lista cubre la mayor parte del valor:
- Modela la amenaza. ¿Quién querría romper esto y qué ganaría? Sin actor no hay foco.
- Enumera las fronteras de confianza. Cada punto donde un dato cruza de “no confiable” a “confiable” es un sitio para validar — o para colarse.
- Asume entrada hostil. Nulo, vacío, gigante, con caracteres raros, fuera de rango. ¿Qué se rompe?
- Caza los fallos silenciosos. Busca cada
catchque no relanza ni registra, cada error que se traga, cada valor por defecto que oculta un problema. - Cuestiona el aislamiento. En sistemas multi-inquilino, ¿puede un tenant ver datos de otro por algún camino? Las fugas de aislamiento casi nunca son obvias.
- Verifica lo que verificas. Cada hallazgo, confírmalo con una segunda mirada independiente antes de darlo por bueno.
Preguntas frecuentes
¿Qué es una revisión de código adversarial?
Es una revisión cuyo objetivo es demostrar que el código está roto, no confirmar que funciona. Adopta la mentalidad del atacante —asume entrada hostil, concurrencia en el peor momento y dependencias que fallan— para encontrar fallos de diseño y de seguridad que la auto-revisión, sesgada hacia la confirmación, se deja pasar.
¿Por qué es mala idea revisar tu propio código buscando que esté bien?
Por el sesgo de confirmación: si tu objetivo es confirmar que funciona, tu cerebro encuentra evidencia de ello y omite lo que lo contradice. Lees lo que quisiste escribir, no lo que escribiste. Por eso la auto-revisión caza erratas pero se pierde los fallos de diseño y los casos límite.
¿Por qué 'cero hallazgos críticos' no significa que el código sea seguro?
Porque un informe solo refleja lo que se miró: un crítico no encontrado no aparece. Además, varios hallazgos de severidad media o baja se componen —un log que filtra, una validación floja y un permiso de más pueden encadenar un incidente que ningún crítico aislado habría causado. La pregunta útil es qué no se auditó.
¿Se pueden usar agentes de IA para revisión adversarial?
Sí, y encajan bien porque el patrón de varios revisores independientes con lentes distintas se paraleliza de forma natural. La clave es el prompt adversarial: pedirle a cada agente que asuma que el código es culpable y trate de probarlo, no que confirme que está bien. Luego se aplica una regla de mayoría sobre sus veredictos.
Conclusión
La revisión adversarial no es desconfianza, es disciplina. Es aceptar que tu cerebro está cableado para ver lo que espera ver, y construir un proceso que compense ese sesgo: cambiar la pregunta de “¿funciona?” a “¿cómo lo rompo?”, revisar por lentes en lugar de todo a la vez, verificar con varias miradas independientes, clasificar por severidad y no confundir “cero críticos” con “seguro”.
Hazlo como práctica permanente, no como ritual previo a una release. El atacante va a auditar tu código tarde o temprano. La única pregunta es si lo haces tú primero.