100 días construyendo un scanner de seguridad: lo que aprendimos
Lecciones reales de seguridad en código después de escanear cientos de repos de GitHub. Lo que más falla, lo que sorprende, y lo que cambiaría.
Rod
Founder & Developer
Llevamos 100 días construyendo Data Hogo y escaneando repos de GitHub. Cientos de repos, de todos los tamaños: side projects de fin de semana, startups en producción, proyectos open source con miles de estrellas.
Esto es lo que aprendimos. No teoría — hallazgos reales, patrones reales, y las lecciones de seguridad en código que nadie te cuenta en un tutorial.
La sorpresa número uno: los secretos están en todos lados
Esto no lo esperábamos con tanta frecuencia.
Pensábamos que los devs sabían ya no poner API keys directamente en el código. La realidad es diferente. En una muestra de repos públicos, encontramos credenciales expuestas en una proporción alarmante. No secretos de test — secretos de producción. Stripe keys con sk_live_, tokens de Twilio, contraseñas de base de datos.
El patrón más común:
// MAL: key hardcodeada — esto va al historial de Git para siempre
const client = new OpenAI({ apiKey: "sk-proj-xxxxxxxxxxxxx" });
// BIEN: siempre desde variables de entorno
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });Lo que aprendimos: el problema no es ignorancia. Es el flujo. Estás probando rápido, pegas la key para que funcione, dices "luego lo muevo a .env", y haces commit antes de acordarte. Git no olvida.
Si ya commiteaste un secreto, cambiar el .env no borra nada. Tienes que rotar la credencial y limpiar el historial. Verifica si algún secreto quedó expuesto en tu repo →
El segundo patrón: dependencias que nadie actualiza
El 60% de los repos que escaneamos tenían al menos una dependencia con un CVE conocido. No porque los devs no sepan que deben actualizar — sino porque npm audit grita tanto que terminas ignorándolo.
La diferencia entre un CVE molesto y uno peligroso importa mucho. Aprendimos a priorizar así:
- CVE en una dependencia que toca datos de usuario o hace requests externos: arregla hoy
- CVE en una dependencia de build o desarrollo: arregla esta semana
- CVE en una dependencia transitiva con vector de ataque complejo: pon en el backlog, monitorea
# Corre esto y mira qué sale
npm audit --audit-level=high
# Si tienes muchas alertas, filtra por las que realmente importan
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical" or .value.severity == "high")'Lo que cambiaría: haberle prestado atención a npm audit desde el día uno en lugar de acumular deuda.
Lección de seguridad que más dolió: la falsa sensación de "es solo un side project"
Esta la escuchamos mucho cuando hablamos con devs. "Es solo un side project, no le va a atacar nadie."
La neta es que los atacantes no saben ni les importa si es tu side project. Los scripts automatizados escanean GitHub en busca de patrones — API keys, tokens, credenciales. No distinguen entre Netflix y tu app de notas personal.
Encontramos repos abandonados con Stripe keys activas que habían acumulado cargos. El dueño no lo sabía. El repo llevaba dos años sin un commit.
Lo que más nos sorprendió de las configuraciones
Pensábamos que el gran problema sería código complejo con bugs sutiles. En realidad, la mayoría de hallazgos críticos venían de configuración mal hecha, no de lógica complicada.
Los patrones de configuración que más aparecieron:
CORS abierto a todo:
// MAL: cualquier origen puede hacer requests a tu API
app.use(cors({ origin: "*" }));
// BIEN: solo tus dominios
app.use(cors({
origin: ["https://tuapp.com", "https://www.tuapp.com"],
credentials: true,
}));Debug mode en producción:
// MAL: stack traces visibles para cualquiera
app.use((err, req, res, next) => {
res.json({ error: err.message, stack: err.stack }); // nunca en prod
});
// BIEN: mensaje genérico para el usuario, log detallado para ti
app.use((err, req, res, next) => {
console.error(err); // va a tus logs
res.status(500).json({ error: "Algo salió mal" });
});Security headers faltantes: La mayoría de apps no tiene Content-Security-Policy, X-Frame-Options, ni Strict-Transport-Security. Puedes revisar los headers de tu URL deployada en menos de un minuto.
Lo que aprendimos sobre cómo los devs leen los resultados de seguridad
Esto fue un aprendizaje de producto más que de seguridad, pero es relevante.
Cuando un scanner te devuelve una lista de CVE con scores CVSS y referencias a NVD, la mayoría de devs cierra la pestaña. No porque no les importe — sino porque no saben qué hacer con esa información.
Lo que funciona es explicar en lenguaje directo: qué está mal, por qué importa en tu caso específico, y los pasos concretos para arreglarlo. Eso es lo que intentamos hacer con cada hallazgo en Data Hogo.
El reporte State of Software Security 2025 de Veracode encontró que las organizaciones que usan análisis automatizado cierran el 90% de sus vulnerabilidades en menos de un mes. Las que no lo usan: 14 meses en promedio. La diferencia no es conocimiento — es visibilidad.
Los 5 hallazgos que más se repiten
Después de cientos de escaneos, estos cinco aparecen constantemente:
- Secretos en el código — API keys, tokens, passwords en archivos que no deberían tenerlos
- Dependencias con CVEs altos o críticos — especialmente en proyectos que no se han tocado en meses
- Falta de validación de input — el código confía en lo que le mandan sin verificar
- CORS mal configurado — demasiado permisivo, o con credenciales habilitadas sin restricción de origen
- Security headers faltantes — la app no le dice al browser cómo debe comportarse
Si estás leyendo esto y no has checado tu repo todavía, probablemente tienes al menos dos de estos cinco. No como insulto — como estadística.
Qué haríamos diferente desde el día uno
Si empezáramos de nuevo, esto lo haríamos desde el primer commit:
- Un
.env.exampledocumentado con todas las variables requeridas, sin valores - Un
.gitignoreque incluya.env*antes del primer commit npm auditcomo parte del CI, no como tarea pendiente- Un escaneo de seguridad básico antes del primer deploy a producción
Nada de esto requiere experiencia en seguridad. Solo un checklist.
Qué sigue en Data Hogo
Seguimos escaneando, aprendiendo y mejorando las reglas de detección. Cada nuevo patrón que encontramos en repos reales lo convierte en una regla nueva.
Si quieres ver qué tiene tu repo, el plan gratuito cubre 3 escaneos al mes sin tarjeta de crédito. El análisis toma menos de 5 minutos y devuelve cada hallazgo explicado en español.
Preguntas frecuentes
¿Cuáles son los errores de seguridad más comunes en código real?
Secretos y API keys commiteados directamente en el código, dependencias con CVEs sin actualizar, y endpoints de API sin verificar autenticación. Estos tres patrones aparecen en la mayoría de repos que escaneamos, independientemente del framework o tamaño del proyecto.
¿Los proyectos pequeños tienen más vulnerabilidades que los grandes?
No necesariamente más vulnerabilidades, pero sí tienden a tener los tipos más peligrosos. Los proyectos pequeños frecuentemente tienen secretos expuestos y falta de autenticación porque la velocidad de desarrollo es prioridad. Los proyectos grandes tienen procesos pero más superficie de ataque.
¿Con qué frecuencia debo escanear mi repo por seguridad?
Mínimo una vez antes de cualquier deploy a producción, y cada vez que agregues dependencias nuevas o integres un servicio externo. Idealmente, un scan automático en cada PR para detectar problemas antes de que lleguen a main.
¿Vale la pena revisar la seguridad de un proyecto pequeño?
Sí, especialmente si maneja datos de usuarios, pagos, o cualquier información sensible. El tamaño del proyecto no determina el valor de lo que protege. Una app pequeña con 500 usuarios tiene datos reales que proteger.