🔐Ciberseguridad

Secret scanning: cómo detectar y revocar credenciales expuestas en tus proyectos

Felipe••17 min de lectura
Aprende a detectar secretos con Gitleaks, revisar el historial Git y coordinar revocación y reemplazo, con un laboratorio local y GitLab CI/CD.

Borras una API key de un archivo, haces commit y el repositorio vuelve a verse limpio. Sin embargo, una copia anterior sigue en Git, otra quedó dentro de un paquete y la credencial todavía permite acceder al servicio. Has retirado una referencia visible; no has cancelado el acceso.

Esa diferencia explica el Secrets Revocation Program de Truffle Security. Su anuncio oficial es del 4 de noviembre de 2026; BleepingComputer lo publicó el día 5. La compañía informó de aproximadamente 1,4 millones de secretos expuestos que había verificado como activos, y propuso coordinar su revocación con las organizaciones emisoras. La cifra describe el conjunto identificado al anunciar el programa, no 1,4 millones de revocaciones ya completadas. Anuncio oficial, cobertura de BleepingComputer.

Vamos a convertir ese problema en un procedimiento para proyectos de desarrollo: detectar, interpretar el hallazgo, cortar el acceso y reemplazar la credencial. Después comprobaremos con un repositorio local por qué eliminar un archivo no limpia su historial.

Qué cuenta como secreto

Un secreto es un valor cuyo conocimiento o posesión permite realizar una operación restringida o proteger información. Por ejemplo:

  • Una API key privada que permite consumir un servicio.
  • Un token de publicación en npm o de acceso a un repositorio.
  • Una contraseña de base de datos.
  • Una clave privada de firma o un secreto de cliente OAuth.
  • Una URL que incorpora credenciales o actúa como enlace de acceso privilegiado.

El nombre no determina la sensibilidad. Un client_id público no suele ser un secreto; una variable llamada CONFIG podría contener una contraseña completa. Tampoco todas las API keys son confidenciales: depende de cómo las define y restringe el proveedor.

Aquí nos centraremos en credenciales de acceso. Las claves de cifrado también son secretos, pero destruirlas sin una estrategia de recuperación puede hacer irrecuperables los datos. Su ciclo de vida requiere decisiones adicionales. OWASP: Secrets Management Cheat Sheet.

La exposición no termina en el archivo actual

Un secreto puede copiarse mucho antes de que alguien detecte la filtración. Conviene pensar en los lugares que reciben el código y sus derivados.

Git y el historial de commits

Agregar .env a .gitignore no retira versiones ya registradas. Un commit posterior que borra una línea tampoco modifica los commits anteriores. Además, pueden existir clones, forks, referencias de merge requests y copias fuera de tu control.

Reescribir el historial es una operación de limpieza que requiere coordinación. No es una alternativa a revocar la credencial: quien ya la copió no necesita volver a descargar el repositorio. GitHub recomienda empezar por revocar o rotar el secreto antes de considerar esa limpieza. GitHub: retirar datos sensibles de un repositorio.

Paquetes npm

La superficie de publicación no siempre coincide con lo que revisaste en Git. Un archivo generado, una configuración de prueba o un bundle pueden entrar en el paquete por una selección de archivos demasiado amplia.

Define deliberadamente el campo files de package.json y revisa las exclusiones. Comprueba el contenido que se empaquetará; por ejemplo, npm pack --dry-run --ignore-scripts permite inspeccionar la selección sin ejecutar scripts de empaquetado. Si tu publicación genera archivos mediante scripts, esa vista previa no sustituye revisar también el artefacto final construido. npm: files, npm pack.

Imágenes Docker

Las capas de una imagen conservan cambios anteriores. Copiar un archivo con credenciales en una capa y borrarlo en otra no elimina necesariamente su contenido de las capas distribuidas. Docker: capas de una imagen.

Para secretos necesarios durante el build, utiliza mecanismos diseñados para ello, como los secret mounts de BuildKit. No los incrustes en ARG, ENV o un COPY del contexto. El montaje tampoco evita que un script copie el valor a un archivo de salida: el código de construcción sigue siendo relevante. Docker: build secrets.

Pipelines, logs y artefactos

Un pipeline puede filtrar secretos mediante printenv, trazas de shell, mensajes de error, informes o artefactos descargables. El enmascaramiento ayuda, pero no es una barrera frente a un job malicioso: el proceso que recibe una variable puede leerla y transmitirla.

GitLab distingue variables enmascaradas, ocultas y protegidas. Revisa qué jobs pueden recibirlas, especialmente ante cambios no confiables. No basta con que el valor aparezca como [MASKED] en una salida concreta. GitLab: seguridad de variables CI/CD.

Qué hace realmente secret scanning

Secret scanning busca indicios de credenciales en fuentes como archivos, historial Git o artefactos. El resultado es un conjunto de hallazgos que debe interpretarse según su origen y el detector utilizado.

Hay tres ideas que conviene separar:

TécnicaQué examinaQué no demuestra por sí sola
PatronesPrefijos, formatos, asignaciones y contexto mediante reglasQue la cadena sea una credencial real o siga vigente
EntropíaDiversidad estadística de caracteres, como señal de aparente aleatoriedadQue una cadena aleatoria otorgue acceso
VerificaciónRespuesta del proveedor a una comprobación con la credencialQue nunca haya sido abusada o cuál es todo su impacto

Una cadena de prueba puede coincidir con un patrón. Un hash de archivo puede parecer aleatorio. Una contraseña corta y débil puede no superar un umbral de entropía. Estas señales ayudan a encontrar candidatos; no sustituyen el contexto.

Gitleaks y TruffleHog

Gitleaks permite analizar archivos y cambios históricos de Git mediante reglas, expresiones regulares y filtros, incluidos criterios de entropía. Es útil para controles locales y CI sin tener que probar los valores contra sus emisores. Repositorio oficial de Gitleaks.

TruffleHog incorpora detectores y, para los tipos compatibles, verificadores que pueden consultar APIs de proveedores. Su documentación distingue resultados verified, unverified y unknown; un error de verificación no demuestra que la credencial sea inocua. Filtrar solo verified puede dejar fuera candidatos que merecen revisión. Repositorio oficial de TruffleHog.

La verificación puede generar tráfico, eventos de auditoría o alertas. Úsala solo sobre credenciales y sistemas que estás autorizado a evaluar. TruffleHog dispone de --no-verification cuando corresponde evitar esas comprobaciones. No necesitas validar una credencial ajena para informar responsablemente de su exposición.

Los límites del resultado

Un informe sin coincidencias significa «no se detectaron coincidencias con este alcance y configuración». No demuestra ausencia de secretos en ramas no descargadas, archivos excluidos, datos codificados, paquetes o imágenes que no analizaste.

Un falso positivo es una alerta sobre algo que no es un secreto real; un falso negativo, un secreto que el análisis no detecta.

Antes de descartar un hallazgo, identifica el detector, ubicación, commit, proveedor probable y propietario. Si es una fixture, confirma que no corresponde a una credencial auténtica. Documenta excepciones acotadas; ignorar toda una carpeta de configuración para silenciar una alerta puede ocultar filtraciones futuras.

Qué aporta el programa de revocación

El programa de Truffle Security propone colaborar con los emisores: la empresa que acepta una API key es quien puede invalidarla. El anuncio describe dos vías, una integración con un endpoint de revocación y otra mediante notificaciones al equipo emisor.

Esto reduce la distancia entre descubrir una credencial y conseguir que deje de funcionar. No significa que instalar TruffleHog revoque automáticamente cualquier hallazgo, ni que todos los proveedores participen. Tampoco reemplaza el inventario y la respuesta a incidentes de tu organización. Funcionamiento anunciado por Truffle Security.

Laboratorio: borrar el archivo no borra la exposición histórica

Utilizaremos Git, Docker y Gitleaks 8.30.1, con un valor que no pertenece a ningún servicio. No crearemos tokens auténticos, no conectaremos el repositorio a un remoto y los contenedores de análisis se ejecutarán sin red.

La regla es deliberadamente artificial. Su función es hacer el resultado reproducible; no representa la cobertura real de los detectores incluidos en Gitleaks.

1. Crear un repositorio ficticio

En una carpeta de trabajo nueva:

mkdir laboratorio-secretos
cd laboratorio-secretos
mkdir reports

git init -b main repo
cd repo

git config user.name 'Laboratorio'
git config user.email 'laboratorio@example.invalid'
git config commit.gpgsign false

Crea .gitleaks.toml:

title = "Solo laboratorio: credencial ficticia"

[[rules]]
id = "demo-falsa"
description = "Marcador didáctico sin proveedor real"
regex = '''DEMO_API_KEY=(FAKE_ONLY_[A-Z0-9]{16})'''
secretGroup = 1
keywords = ["FAKE_ONLY_"]

La expresión reconoce una asignación concreta y secretGroup = 1 identifica el grupo que contiene el valor. No añadimos un umbral de entropía: queremos detectar este marcador por su formato.

Ahora registra el archivo ficticio:

printf '%s\n' 'DEMO_API_KEY=FAKE_ONLY_0123456789ABCDEF' > config.env

git add .gitleaks.toml config.env
git commit -m 'Añadir configuración ficticia'

2. Ejecutar el análisis

Descarga la imagen oficial y comprueba la versión:

docker pull ghcr.io/gitleaks/gitleaks:v8.30.1

docker run --rm --network none \
  ghcr.io/gitleaks/gitleaks:v8.30.1 version

Desde repo, define una función para limitar los montajes al laboratorio. El repositorio será de solo lectura y los informes se escribirán fuera de él:

gitleaks_local() {
  docker run --rm --network none \
    -v "$PWD:/repo:ro" \
    -v "$PWD/../reports:/reports" \
    ghcr.io/gitleaks/gitleaks:v8.30.1 "$@"
}

Analiza el historial:

gitleaks_local git /repo \
  --config /repo/.gitleaks.toml \
  --log-opts='--all' \
  --redact=100 \
  --no-banner --no-color \
  --report-format json \
  --report-path /reports/antes.json

echo "Código de salida: $?"

En esta ejecución el código fue 1 porque hubo una coincidencia. Revisa siempre el informe y los logs: un fallo del comando no debe interpretarse automáticamente como una filtración. En un script con set -e, ese resultado detendrá la ejecución; en CI es precisamente el comportamiento que buscamos ante un hallazgo.

--redact=100 evita mostrar el valor detectado. El informe generado en esta prueba contiene REDACTED en el campo Secret. Aun redactado, un informe puede revelar rutas y metadatos sensibles, por lo que necesita acceso restringido.

3. Eliminar el archivo y comparar alcances

git rm config.env
git commit -m 'Eliminar el archivo de configuración'

gitleaks_local dir /repo \
  --config /repo/.gitleaks.toml \
  --redact=100 \
  --no-banner --no-color \
  --report-format json \
  --report-path /reports/directorio.json

echo "Código de salida: $?"

Vuelve a ejecutar el análisis Git, ahora guardando otro informe:

gitleaks_local git /repo \
  --config /repo/.gitleaks.toml \
  --log-opts='--all' \
  --redact=100 \
  --no-banner --no-color \
  --report-format json \
  --report-path /reports/historial.json

echo "Código de salida: $?"

Estos fueron los resultados observados en el laboratorio:

AnálisisCoincidenciasCódigo de salida
Historial antes de borrar el archivo11
Directorio después de borrarlo00
Historial después de borrarlo11

Los informes históricos identificaron la regla demo-falsa, config.env, línea 1. El segundo commit quitó el archivo del estado actual; el primero sigue siendo alcanzable desde la rama. Gitleaks analiza los cambios de Git, no solo los archivos que ves al abrir la carpeta. Gitleaks: comandos Git y directorio.

No hubo credenciales reales ni revocación en este laboratorio. Tampoco hace falta una captura de terminal para interpretar el resultado: la tabla resume las salidas ejecutadas y puedes revisar los JSON en reports.

Los contenedores se eliminan al terminar mediante --rm. Para limpiar, elimina únicamente la carpeta ficticia laboratorio-secretos cuando ya no necesites los informes; no ejecutes una limpieza de historial sobre un proyecto real siguiendo este ejemplo.

Del hallazgo a la revocación real

El scanner responde dónde encontró un candidato. El procedimiento de respuesta debe resolver quién controla la credencial, qué permite hacer y cómo impedir su uso.

flowchart TB
    A[Hallazgo del scanner] --> B[Identificar emisor, propietario y alcance]
    B --> C[Evaluar exposición y contener el acceso]
    C --> D[Revocar la credencial comprometida]
    D --> E[Distribuir un reemplazo con permisos mínimos]
    E --> F[Comprobar servicio y rechazo de la credencial anterior]
    F --> G[Investigar actividad y limpiar copias controladas]

El diagrama prioriza la contención de una credencial comprometida. La coordinación exacta depende del riesgo y de las posibilidades del proveedor; no es una instrucción para apagar indiscriminadamente servicios productivos.

1. Identificar y contener

Registra una referencia segura al hallazgo, su ubicación y ventana de exposición. No pegues el valor completo en un ticket o chat. Identifica cuenta, permisos, entorno y sistemas consumidores.

Si la credencial está expuesta y puede abusarse de ella, prioriza su revocación o desactivación en el emisor. Coordina recuperación y posibles interrupciones, pero no mantengas indefinidamente un acceso comprometido para evitar cambios operativos. Estas son decisiones de respuesta basadas en riesgo, alineadas con la gestión del ciclo de vida descrita por OWASP.

2. Distinguir revocación, rotación y reemplazo

AcciónResultado que debes comprobar
RevocarEl emisor deja de aceptar la credencial anterior
RotarSe sustituye la credencial como parte de su ciclo de vida y se retira la anterior
Reemplazar en la aplicaciónLos consumidores reciben y utilizan el nuevo valor

Crear una segunda API key sin invalidar la expuesta no cierra el incidente. Cambiar el valor en un gestor tampoco revoca automáticamente el acceso en el proveedor.

En una rotación programada puede existir un solapamiento controlado para migrar consumidores. Ante una filtración, ese periodo conserva una oportunidad de abuso: la política de emergencia puede exigir cortarlo. Infisical, por ejemplo, documenta modelos de rotación con y sin solapamiento según el proveedor; hay que entender cuál se está usando. Infisical: secret rotation.

3. Verificar y revisar el impacto

Confirma la invalidación mediante el estado del proveedor o una comprobación mínima, autorizada y documentada. Comprueba también que los servicios legítimos reciben el reemplazo y funcionan. No consideres cerrado el incidente porque el pipeline vuelve a estar verde.

Revisa auditoría, accesos y cambios durante la ventana de exposición: una credencial revocada no deshace acciones anteriores ni elimina credenciales adicionales que alguien haya creado. Después trata las copias que controlas: historial, paquetes, imágenes, logs y artefactos. La limpieza reduce exposición residual; no permite recuperar todas las copias descargadas.

Incorporar el control a GitLab CI/CD

El siguiente job ejecuta Gitleaks en pipelines de merge request, la rama predeterminada y ejecuciones programadas. Necesita un runner compatible con imágenes de contenedor. Es un ejemplo para tu proyecto; no cambia el pipeline de este blog.

En un proyecto real, si utilizas .gitleaks.toml, parte de las reglas incluidas en vez de copiar la configuración limitada del laboratorio:

title = "Reglas del proyecto"

[extend]
useDefault = true

Ejemplo de .gitlab-ci.yml:

stages:
  - test

secret_scanning:
  stage: test
  image:
    name: ghcr.io/gitleaks/gitleaks:v8.30.1
    entrypoint: [""]
  variables:
    GIT_DEPTH: "0"
  script:
    - >-
      gitleaks git .
      --config .gitleaks.toml
      --log-opts="--all"
      --redact=100
      --no-banner
      --no-color
      --report-format json
      --report-path gitleaks-report.json
  allow_failure: false
  artifacts:
    when: always
    access: maintainer
    expire_in: 1 day
    paths:
      - gitleaks-report.json
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'

GIT_DEPTH: "0" evita que un clon superficial limite el historial disponible. --all cubre las referencias presentes en ese checkout; no inventa acceso a repositorios, submódulos o artefactos externos. GitLab: profundidad del clon.

El informe se conserva incluso si el job falla y su acceso por UI/API se limita a Maintainer u Owner. artifacts:access: maintainer requiere GitLab 18.4 o superior; revisa también visibilidad de jobs y permisos de job tokens. Esto no convierte el JSON nativo de Gitleaks en el formato de reporte de seguridad integrado de GitLab. GitLab: artifacts.

Integra estas claves con tus stages, workflow y reglas existentes; no reemplaces a ciegas todo el pipeline. Configura además la política de merge para exigir el control y protege cambios en el job, reglas y excepciones. Un atacante que pueda eliminar el análisis puede conseguir un pipeline verde sin haber pasado esa comprobación.

El job se ejecuta después del push: puede bloquear la integración, pero el valor ya llegó al remoto. Complementa el control con revisión local, hooks o protección de pushes cuando estén disponibles.

La imagen está fijada por versión. Para controlar su inmutabilidad puedes fijar también el digest validado y actualizarlo deliberadamente. El job no necesita una credencial de producción para examinar el checkout.

Ejecuté los comandos del scanner localmente; no ejecuté este pipeline en una instancia GitLab. GitLab también ofrece su propia integración de Pipeline Secret Detection, cuyo alcance y funcionalidades deben revisarse para tu versión y plan.

Hallazgos históricos y excepciones

Escanear todo el historial puede volver a señalar una credencial que ya revocaste. Eso no significa que la revocación haya fallado: la cadena sigue en Git.

Gitleaks admite baselines para omitir hallazgos conocidos. Úsalas después de clasificar y resolver los casos, con evidencia y revisión; no generes una baseline automática para esconder todo lo encontrado. Las exclusiones también deben ser específicas y revisables. Gitleaks: baselines.

Node.js y gestores: reducir las copias que tienes que perseguir

La aplicación debe recibir únicamente las credenciales que necesita. Un módulo mínimo puede validar su presencia sin imprimirlas:

// config.mjs: solo se importa desde el servidor.
const billingApiToken = process.env.BILLING_API_TOKEN;

if (!billingApiToken || billingApiToken.trim() === '') {
  throw new Error('Falta la configuración BILLING_API_TOKEN');
}

export const config = Object.freeze({ billingApiToken });

process.env entrega valores al proceso; no constituye un almacén seguro ni valida que el token esté vigente. Tampoco Object.freeze() cifra el objeto: solo evita modificar sus propiedades directamente. No serialices config, no registres cabeceras Authorization ni incluyas este módulo en el bundle del navegador. Node.js: process.env.

La distinción entre almacenamiento y entrega ya está desarrollada en Variables de entorno no son un gestor de secretos. Aquí importa su efecto operativo: cuanto menos copies una credencial, menos superficies tendrás que localizar durante un incidente.

Infisical puede centralizar valores, controlar su entrega y automatizar rotaciones compatibles. También tiene funciones de scanning, pero almacenar, detectar y revocar siguen siendo responsabilidades diferentes. Revisa su documentación de scanning y la guía práctica de Infisical en Node.js para la integración.

Una identidad capaz de leer todos los secretos sigue siendo un riesgo aunque exista un gestor. Limita entornos, permisos y duración; comprueba cómo reciben los consumidores una actualización y cómo se retira el valor anterior en el proveedor.

Un informe puede convertirse en otra filtración al pasarlo a una IA

Un agente con acceso al repositorio puede leer el historial, ejecutar el scanner y copiar sus resultados al contexto de un modelo. Si recibe el informe completo con valores sin ocultar, el proceso de diagnóstico crea otra exposición.

Para pedir ayuda con la clasificación, suele bastar un resumen como este:

Detector: credencial del proveedor de facturación
Ubicación: configuración de backend, commit identificado internamente
Entorno: producción
Permisos conocidos: emisión de documentos
Valor: omitido
Estado: revocación confirmada por el emisor
Necesito revisar consumidores pendientes y la estrategia de reemplazo.

Es un ejemplo de resumen, no un hallazgo real. Un asistente puede ayudar a ordenar tareas; no necesita probar la credencial ni recibir permisos de revocación para hacerlo. Mantén las acciones sensibles en herramientas y flujos autorizados. El artículo sobre agentes de IA y secretos explica los controles de acceso y ejecución con más detalle.

Detectar es el comienzo; cerrar el acceso es el objetivo

El laboratorio dejó una diferencia visible: cero coincidencias en el directorio y una en el historial. En un incidente real, a esa comprobación le falta la más importante: que el proveedor ya no acepte la credencial expuesta.

Un flujo útil conecta el hallazgo con un propietario, una decisión de contención, un reemplazo verificado y evidencia de cierre. Los controles locales y CI reducen errores, los gestores limitan la distribución y la revocación corta el acceso. Ninguno reemplaza por sí solo a los demás.

Referencias

#secret scanning#Gitleaks#TruffleHog#revocación de credenciales#GitLab CI/CD#Infisical#Node.js

Artículos relacionados