Skip to content
This page has been auto-translated and may contain errors.View in English

Triaje de bugs

Llega un mensaje al chat del equipo:

Creo que los clientes pueden ver los pedidos de otros clientes.

Eso podría ser urgente. Podría ser un malentendido. Todavía nadie lo sabe.

El triaje de bugs es el trabajo que ocurre entre "alguien encontró algo" y "el equipo sabe qué hacer a continuación".

Para los bugs de seguridad, el triaje responde cuatro preguntas:

  1. ¿Podemos reproducirlo?
  2. ¿Qué daño causa?
  3. ¿Qué tan difícil es explotarlo?
  4. ¿Qué pasa después?

Reproduce el bug

Un hallazgo de seguridad es solo una afirmación hasta que alguien logra reproducirlo a propósito.

Empieza por la ruta vulnerable:

Vulnerable
js
app.get('/orders/:id', async (req, res) => {
  const order = await db.orders.findById(req.params.id)
  res.json(order)
})

Ahora escribe la reproducción:

bash
# Con sesión iniciada como cliente 4102
curl -i https://app.example.com/orders/88213 \
  -H "Cookie: sid=<sesión del cliente 4102>"

# Esperado: 403 o 404
# Real: 200, con el pedido de otro cliente

Una buena reproducción incluye la cuenta, la solicitud, el resultado esperado y el resultado real.

"Cambias el id y se rompe" es una nota. El bloque de arriba es algo que otro desarrollador puede ejecutar.

Prueba solo donde tengas permiso

Ejecuta reproducciones únicamente contra sistemas que sean tuyos, que mantengas, o para los que tengas permiso escrito de hacer pruebas.

Hacer la misma solicitud contra la app de otra persona no es triaje.

JunoReproduce el bug Una reproducción es la receta para hacer que el bug vuelva a suceder.

Escribe la cuenta, la solicitud, qué debería haber pasado y qué pasó en realidad. Si otro desarrollador puede seguirla sin tener que preguntar nada, tienes un reporte útil.

JunoReproduce el bug Reduce el bug a la solicitud más pequeña que todavía lo demuestre. Quita encabezados, pasos y datos extra hasta que quitar cualquier otra cosa haga que el comportamiento desaparezca.

Esa solicitud más pequeña es más fácil de probar, más fácil de arreglar y más difícil de descartar. También te dice qué parte del sistema es la que realmente está tomando la decisión insegura.

JunoReproduce el bug Reproduce primero cuando se sospecha de un bug. Contén primero cuando el bug ya está siendo explotado.

Si los logs muestran cuentas desconocidas recorriendo ids de pedidos, o si los clientes reportan haber visto datos que nunca fueron suyos, primero preserva la evidencia y limita el acceso.

No gastes la mejor hora del incidente demostrándote a ti mismo que el bug existe. La reproducción puede esperar lo suficiente como para que los logs no se evaporen, y los logs adoran evaporarse. Es uno de sus pasatiempos menos encantadores.

Explica el impacto

Impacto significa qué termina obteniendo o haciendo la persona que usa el bug.

Para el bug del pedido, el impacto podría ser:

Cualquier cliente con sesión iniciada puede leer la dirección de entrega y el historial de pedidos de otro cliente.

Esa frase es mejor que:

IDOR en /orders/:id.

IDOR es el nombre técnico de seguridad: referencia directa insegura a un objeto (insecure direct object reference). Significa que una solicitud incluye el id de un objeto y el servidor no verifica si quien la hace tiene permiso de usar ese objeto.

El nombre ayuda a los ingenieros a clasificar el bug. El impacto ayuda a todos a entender por qué importa.

Pregunta:

  • ¿Qué dato o acción queda expuesto?
  • ¿De quién son esos datos o esa acción?
  • ¿A cuántas personas podría afectar?
  • ¿Qué podría habilitar esto después?

La severidad empieza por el daño, no por lo ingenioso que sea el bug.

JunoExplica el impacto El impacto es el daño expresado en lenguaje simple. ¿Qué puede leer, cambiar o hacer la persona?

Los nombres técnicos de seguridad son útiles, pero no bastan por sí solos. "Cualquier cliente puede leer la dirección de otro cliente" le dice a más gente por qué importa que "IDOR".

JunoExplica el impacto Cuando no sepas el número exacto, da un límite aproximado. "Todos los pedidos desde marzo, unos 40,000" es mejor que dejar el campo vacío.

Luego sigue el rastro del daño un paso más. Una dirección de correo expuesta puede facilitar el phishing. Un token de restablecimiento expuesto puede convertirse en el robo de una cuenta. Un paso mantiene el reporte con los pies en la tierra; cinco pasos lo convierten en una historia inventada con etiqueta de severidad.

JunoExplica el impacto Algunos hallazgos no tienen un impacto alcanzable hoy, y forzar uno en el reporte empeora toda la cola de trabajo. Que falte una bandera de cookie en una ruta que no acepta escrituras entre sitios es una brecha real, pero no una historia viva de robo de cuenta.

Regístralo como "sin impacto alcanzable por ahora" y luego describe qué condición haría que sí importara. Esa condición futura es la parte útil: es la pista que alguien va a necesitar cuando la ruta cambie dentro de seis meses y ese hallazgo de bajo riesgo deje de ser algo puramente decorativo.

Evalúa la explotabilidad

El impacto te dice qué puede hacer el bug. La explotabilidad te dice qué se necesita para lograrlo.

Para el bug del pedido, el atacante necesita:

  • una cuenta de cliente normal
  • un id de pedido válido o adivinable
  • una solicitud

Eso es altamente alcanzable. Un bug que requiere una cuenta de administrador, acceso físico, un momento preciso y que la víctima haga clic en algo es muy distinto.

Anota las condiciones previas. Eso mantiene concreta la conversación sobre la severidad.

Condiciones previasQué significa
AnónimoCualquiera en internet puede intentarlo.
Con sesión iniciadaSe necesita una cuenta normal.
Rol privilegiadoSe necesita un rol de staff o administrador.
Acción de la víctimaOtra persona debe hacer clic o enviar algo.
Id adivinableEl objetivo se puede encontrar probando valores.
Ventana de tiempoEl bug solo funciona durante un momento acotado.
JunoEvalúa la explotabilidad La explotabilidad es la lista de cosas que deben ser ciertas antes de que el bug funcione.

Anónimo es más fácil de alcanzar que con sesión iniciada. Con sesión iniciada es más fácil de alcanzar que administrador. Una sola solicitud es más fácil de alcanzar que una cadena que necesita sincronización y que otra persona haga clic en algo.

JunoEvalúa la explotabilidad Trata las condiciones previas como afirmaciones que hay que verificar. Si el reporte dice "se requiere administrador", revisa cuántos administradores existen y cómo se otorga ese rol.

Si dice que los ids no se pueden adivinar, mira los ids reales. Una marca de tiempo con un contador al final no deja de ser adivinable solo porque la variable tenga un nombre bonito. Los nombres han traicionado a gente mejor que nosotros.

JunoEvalúa la explotabilidad La explotabilidad cambia cuando cambia el sistema que la rodea. Un hallazgo calificado como bajo riesgo porque necesita una cuenta de staff se convierte en otro hallazgo distinto cuando las herramientas de soporte se abren a socios externos.

Anota la fecha y el motivo junto a la calificación. El tú del futuro, o la persona que herede tu servicio después de tres reorganizaciones, necesita saber qué suposición sostenía esa calificación. Las suposiciones se echan a perder como la leche en un cuarto de servidores.

Decide qué pasa después

Ahora combina las piezas:

CampoEjemplo
ReproducciónEl cliente 4102 solicita el pedido 88213 y recibe el pedido de otro cliente.
ImpactoCualquier cliente con sesión iniciada puede leer la dirección de entrega y el historial de pedidos de otro cliente.
ExplotabilidadCuenta normal, id adivinable, una sola solicitud.
Categoría OWASPA01 Control de acceso roto.
Próxima acciónCorregir antes del lanzamiento, agregar una prueba de propiedad, revisar rutas similares.

La categoría OWASP ayuda a clasificar el bug. La próxima acción convierte el reporte en trabajo concreto.

Para este bug, la solución es verificar la propiedad antes de devolver el pedido:

Fixed
js
app.get('/orders/:id', async (req, res) => {
  const order = await db.orders.findById(req.params.id)

  if (!order || order.userId !== req.user.id) {
    return res.status(404).json({ error: 'Order not found' })
  }

  res.json(order)
})

La prueba correspondiente debe demostrar el camino incorrecto, no solo el camino esperado.

Una buena nota de triaje genera trabajo de seguimiento

Corregir este endpoint.

Agregar una prueba de regresión.

Buscar el mismo patrón en rutas cercanas.

JunoDecide qué pasa después El triaje termina en una decisión. Arreglarlo ahora, arreglarlo después con una razón, o aceptar el riesgo a propósito.

Para este bug del pedido, la decisión debería ser directa: corregirlo antes del lanzamiento, agregar una prueba, y revisar rutas cercanas por si les falta la misma verificación de propiedad.

JunoDecide qué pasa después El seguimiento importa tanto como la corrección misma. Un bug de control de acceso roto en una ruta es una pista para buscar en el resto del recurso.

Busca los manejadores que leen req.params.id y devuelven o modifican datos sin comparar propiedad, rol o tenant. Esa búsqueda convierte un solo reporte en una corrección de toda una clase de bugs.

JunoDecide qué pasa después La corrección tiene tres capas: reparar la ruta, asegurar el bug con una prueba de regresión, y buscar el mismo patrón en otras partes. Si te saltas la tercera, terminas redescubriendo el mismo bug con una URL distinta y una nueva dosis de decepción.

Ten cuidado también con las decisiones sobre las respuestas. Devolver 404 tanto para pedidos inexistentes como para pedidos no autorizados oculta si el id existe. Devolver 403 puede ser más claro para los clientes, pero puede filtrar que el registro sí es real. Elige el comportamiento a propósito y mantenlo consistente por recurso.

Ponlo en práctica

Llega un reporte de bug con una sola línea: "El enlace para restablecer la contraseña sigue funcionando después de haberlo usado."

Hazle el triaje. Completa los cinco campos: reproducción, impacto, explotabilidad, categoría OWASP, próxima acción.

Compara tus respuestas
CampoUna respuesta razonable
ReproducciónSolicita un restablecimiento, sigue el enlace, define una contraseña y luego abre el mismo enlace otra vez. Todavía se acepta y permite definir otra contraseña.
ImpactoCualquiera que vea ese enlace, aunque sea una sola vez, puede tomar el control de la cuenta después. Los enlaces de restablecimiento permanecen mucho tiempo en bandejas de entrada, historial del navegador y correos reenviados.
ExplotabilidadNo se necesita ninguna cuenta ni adivinar nada. Requiere tener acceso al enlace, así que la dificultad depende por completo de a dónde haya viajado ese enlace.
Categoría OWASPA07 Fallas de autenticación. La aplicación no puede establecer de forma confiable quién es alguien cuando una credencial ya usada sigue funcionando.
Próxima acciónInvalidar el token en el primer uso y agregar una expiración si no la tiene. Luego revisar si la verificación de correo y los enlaces de invitación tienen la misma falla.

Vale la pena resaltar dos cosas.

El impacto no es "un token no se invalida", es "alguien puede tomar el control de la cuenta". Un campo que solo repite el bug no aporta nada; el campo existe para decir a quién se perjudica.

Y la próxima acción termina mirando hacia los lados. Los tokens de un solo uso normalmente se generan con código compartido, así que un bug en un flujo suele estar presente también en los demás. Un triaje que corrige exactamente una ruta y se detiene ahí solo hizo la mitad del trabajo.

Hacia dónde va esto

El triaje de bugs cierra la primera sección. Ahora ya puedes preguntarte qué podría salir mal, nombrar bugs comunes en producción, y convertir un hallazgo en trabajo concreto.

A continuación, el manual pasa a la seguridad de entradas y datos, empezando por nunca confíes en la entrada del usuario.