ConstructiCat Logo
CodeBust.
Browse section ▾

Punto ciego de seguridad.

Los asistentes de IA generan código funcional que omite en silencio los controles de seguridad que una persona añadiría por costumbre —validación de entradas, autorización, codificación de salidas, gestión de secretos— porque «compila y devuelve 200» parece estar terminado.

##Signs and Symptoms

Quien revisa detecta un Punto ciego de seguridad cuando el código funciona en el camino feliz pero los controles que un humano consciente de la seguridad añade por reflejo sencillamente no están. El modelo demuestra la funcionalidad, no la defensa. Señales reveladoras:

  • Consultas/comandos construidos por concatenación de cadenas en lugar de parametrizados (puntos de inyección SQL/comandos/LDAP).
  • La entrada del usuario fluye a la salida sin codificación (XSS reflejado/almacenado) o a fs/child_process/fetch sin lista de permitidos (path traversal, SSRF).
  • El endpoint autentica pero nunca autoriza: comprueba quién eres, nunca si puedes tocar este registro (IDOR / autorización a nivel de objeto rota).
  • Secretos incrustados: claves de API, tokens, contraseñas de BD en línea "para que arranque."
  • Controles ausentes por omisión: sin token CSRF, sin cabeceras de seguridad, sin límite de tasa, criptografía débil/heredada (MD5, SHA-1, Math.random() para tokens).
  • Teatro de comentarios: un comentario // validate input o // TODO: add auth ocupa el lugar donde debería estar la comprobación real; OX encontró comentarios redundantes en el 90-100% del código de IA, a menudo sustituyendo a la lógica ausente.
// Manejador de Express generado por IA — parece completo, lleva tres agujeros
app.get("/api/orders/:id", authMiddleware, async (req, res) => {
  const apiKey = "sk_live_8f3b2a91c7d44e0f";            // 1. secreto incrustado
  const order = await db.query(
    `SELECT * FROM orders WHERE id = ${req.params.id}`   // 2. inyección SQL
  );
  res.send(`<h1>Order for ${order.customerName}</h1>`);  // 3. XSS reflejado
  // nota: authMiddleware prueba el login, nunca comprueba order.userId === req.user.id (IDOR)
});

Cuatro vulnerabilidades distintas, cero tests que fallen: el olor es lo que no está ahí.

##Reasons for the Problem

Por qué los modelos lo producen

  • El objetivo del entrenamiento es la verosimilitud, no la seguridad. La predicción del siguiente token optimiza la forma más común de escribir código, y los corpus públicos están saturados de fragmentos de nivel tutorial, con consultas concatenadas y sin autorización. OX Security señala que los modelos "recurren por defecto a enfoques inseguros porque son los más frecuentes en los datos de entrenamiento." Carnegie Mellon descubrió que el 61% de los fragmentos generados por IA funcionan, pero solo ~10,5% superan una revisión de seguridad.
  • "Terminado" lo define el camino feliz. Un LLM optimiza para que la petición visible tenga éxito. Los controles de seguridad no producen ninguna señal positiva en una ejecución rápida, así que se descartan: lo que OX llama "inseguro por ignorancia": código funcional con salvaguardas ausentes porque nadie de los implicados sabía qué hacía falta.
  • Sin contexto del repositorio. El modelo no puede ver tu función auxiliar authorize(), tu esquema de validación ni tu gestor de secretos, así que incrusta una clave literal o se salta la comprobación en lugar de reutilizar un control existente.
  • Adulación + velocidad. Devuelve exactamente lo que le pediste ("añade un endpoint de pedidos"), no el modelo de amenazas que lo rodea, y los cuellos de botella como la revisión y el modelado de amenazas son justo lo que elimina el desarrollo a velocidad de IA: el hallazgo central de OX es que la velocidad, y no la tasa de defectos por línea, es lo que lleva código vulnerable a producción.
  • Obsolescencia por fecha de corte del entrenamiento. Los modelos recurren a patrones de criptografía y de bibliotecas que estaban bien hace años (MD5, SHA-1, opciones de TLS obsoletas, flujos de autenticación antiguos).

Por qué hace daño

  • Explotabilidad directa. Estudios independientes citados por OX: ~62% del código generado por IA llega con una vulnerabilidad; el 86% falló las defensas contra XSS (CSET/Georgetown); CodeRabbit midió 2,74× más XSS que el código humano; Tenzai descubrió que 0 de 15 aplicaciones construidas con IA establecían cabeceras de seguridad o protección CSRF; Escape.tech halló más de 400 secretos expuestos en 5.600 aplicaciones hechas «a ojo».
  • Invisible para los tests y para quien lee. Una comprobación de autorización ausente no tiene ningún test que falle ni ningún diff al que señalar, así que pasa la revisión sin problemas, y el volumen de salida de la IA significa que la revisión no puede escalar para atraparla.
  • Deuda técnica acumulativa. Los datos de GitClear (la refactorización cayó del 25% a <10% de las líneas modificadas, el copiar/pegar subió y ahora supera a las líneas movidas, los bloques duplicados aumentaron ~8×) implican que el mismo patrón inseguro se clona por toda la base de código, multiplicando el radio de impacto de cualquier punto ciego.

##Treatment

Tácticas de revisión y de prompts

  • Nombra el modelo de amenazas en el prompt. "Escribe este endpoint asumiendo que el id está controlado por un atacante; aplica autorización a nivel de objeto, parametriza todas las consultas, codifica toda la salida y lee los secretos desde process.env." Especificar al adversario invierte el comportamiento por defecto.
  • Fuerza la reutilización de los controles existentes. "Usa nuestra función auxiliar authorize(user, 'order', id) y el validador orderSchema: no escribas validación nueva." Esto contrarresta directamente la causa de la falta de contexto del repositorio (y el olor relacionado de Código duplicado).
  • Haz que el modelo ejecute las herramientas. Exige que ejecute semgrep --config p/owasp-top-ten, gitleaks detect, npm audit y tu configuración de eslint-plugin-security, y que arregle lo que marquen, antes de declararlo terminado.
  • Pide los negativos de forma explícita. "Enumera las cuestiones de autenticación, autorización, validación de entradas, codificación de salidas y secretos de este manejador, y muestra dónde se aplica cada una." Un hueco en esa lista es el fallo.
  • Modela las amenazas del diff, no de la línea. Los agujeros peligrosos son omisiones, así que revisa preguntando "¿qué control debería estar aquí y no está?" en lugar de buscar líneas malas.

La refactorización: parametriza la consulta (mata la inyección), codifica la salida (mata el XSS), añade una comprobación de autorización a nivel de objeto (mata el IDOR) y Extrae el secreto incrustado a la configuración:

// DESPUÉS
app.get("/api/orders/:id",
  authMiddleware,
  validate(orderParamsSchema),                    // validación de entradas
  async (req, res) => {
    const order = await db.query(
      "SELECT * FROM orders WHERE id = $1", [req.params.id]   // parametrizada
    );
    if (!order || order.userId !== req.user.id) {            // autorización a nivel de objeto
      return res.sendStatus(404);
    }
    res.json({ customerName: order.customerName });          // salida estructurada, codificada automáticamente
  }
);
// secreto: process.env.STRIPE_API_KEY, cargado una vez al arrancar, nunca en línea

Allí donde el olor se haya propagado por clonación, arréglalo una vez y aplica Extraer función al control (requireOrderOwnership, escapeHtml) para que cada punto de llamada comparta una única implementación auditada en lugar de N copias.

##Detected by