Token de GitHub filtrado: qué hacer y cómo investigarlo
¿Se ha filtrado un PAT o un token OAuth de GitHub? Revócalo, busca su hashed_token en el audit log, revisa IP, países y clonados nuevos y acota lo que alcanzó.
En resumen. Da por hecho que el token se ha usado hasta que los logs demuestren lo contrario. Revócalo (cualquiera que tenga el valor literal puede hacerlo con la API de revocación de credenciales). Calcula su hash (echo -n TOKEN | openssl dgst -sha256 -binary | base64) y busca hashed_token:"…" en el registro de auditoría. Después exporta los eventos Git, que las búsquedas desde la interfaz y la API dejan fuera. Busca países e IP nuevos, ráfagas de clonado y todo lo que haya creado el token: claves, workflows, apps, runners. Por último, comprueba si hay un infostealer en el equipo de su propietario.
Los tokens de acceso personal (PAT) son la credencial que los atacantes reutilizan con más frecuencia contra GitHub. El ghp_… de un desarrollador en un fichero .env, en un log de CI, en un historial de shell pegado en algún sitio o en un volcado de un infostealer da a un atacante el mismo acceso que tiene ese desarrollador a todas las organizaciones para las que el token está autorizado, sin contraseña y sin petición de 2FA. Esta guía cubre la investigación, no solo la rotación.
Primera hora: contén sin destruir evidencias
- Exporta primero y revoca después, si puedes hacer ambas cosas en cuestión de minutos. La revocación queda registrada, así que no destruye nada. Pero en cuanto el token deja de funcionar, el atacante puede pasar a la persistencia que haya dejado instalada, y te interesa tener la foto del "antes". Si el token se está usando de forma abusiva en ese momento, revócalo de inmediato.
- Revoca. El propietario del token puede borrarlo en Settings → Developer settings → Personal access tokens. Con el valor literal, cualquiera puede llamar al endpoint de revocación, que cubre los PAT clásicos y fine-grained, los tokens de OAuth apps y los tokens de usuario de GitHub Apps. En las organizaciones con SAML SSO, un propietario también puede revocar la autorización SSO del token.
- Da por hecho que el origen sigue comprometido. Si el token salió del puesto de trabajo de un desarrollador, lo más probable es que un infostealer se llevara también las cookies del navegador, las claves SSH y las credenciales de las CLI cloud. Reinstalar el equipo va antes que emitir credenciales nuevas.
GitHub ya revoca algunos tokens por ti. Según su documentación sobre la revocación de tokens, un token OAuth, de GitHub App o de acceso personal válido que se sube a un repositorio público o a un gist público se revoca automáticamente. Lo mismo ocurre con un token OAuth o de acceso personal que no se ha usado en un año. Nada de esto ocurre con un token filtrado en un repositorio privado, en un log de compilación o en un sitio de pegado de texto.
Encuentra el token en el registro de auditoría
Los eventos autenticados con token llevan tres campos, documentados en identificar los eventos del registro de auditoría realizados por un token de acceso:
| Campo | Significado |
|---|---|
hashed_token | SHA-256 en Base64 del valor del token |
programmatic_access_type | p. ej. "Personal access token (classic)", "Fine-grained personal access token", OAuth, GitHub App |
token_scopes | Ámbitos de un token clásico, p. ej. repo, workflow, read:org |
Calcula el hash del valor filtrado y búscalo:
echo -n "$LEAKED_TOKEN" | openssl dgst -sha256 -binary | base64
# then, in the audit log search box:
# hashed_token:"Xkr8WMW4...="
Dos salvedades. Las búsquedas desde la interfaz y desde la API REST excluyen los eventos Git, así que tienes que exportarlos para ver los clonados hechos con el token. Y si no tienes el valor (solo una sospecha), trabaja en sentido inverso. En Enterprise Cloud, la exportación del inventario de credenciales lista los valores de hashed_token con sus propietarios (salvo los PAT fine-grained), de modo que puedes asociar a una persona un hash desconocido que aparezca en el log. La entrada del glosario sobre el token con hash da más detalles.
¿Ha usado otra persona el token?
Un token robado se reutiliza en paralelo a su propietario legítimo, así que comparas el token consigo mismo:
- País nuevo para el token. La regla
token-new-countrydel analizador (alta) se activa cuando unhashed_tokenaparece desde un país en el que nunca se había visto, siempre que el token tenga al menos 24 horas de historial en la exportación. La reutilización desde un VPS en el extranjero encaja en este patrón. - IP nueva para el token.
token-new-ip(baja) es ruidosa por sí sola, porque los portátiles cambian de red. Cobra importancia cuando comparte token con otro hallazgo. - Cambio de user agent. El
git/2.39.5 (Apple Git-154)de un desarrollador que de repente aparece junto acurl/8.5.0opython-requestscon el mismo hash. - Hora del día. La actividad a las 02:00 en la zona horaria del propietario, varios días seguidos, merece una pregunta.
- Volumen. Una ráfaga de
git.clonesobre muchos repositorios (consulta exfiltración de repositorios).
Estas detecciones necesitan actor_ip y los datos de país. Si la exportación no tiene IP, activa la visualización de IP, que se aplica también a los eventos existentes, y vuelve a exportar.
¿Qué hizo el token?
Lista todos los eventos que llevan el hash (en el analizador, usa Entidades → Tokens → Ver eventos) y clasifica las acciones en tres grupos:
| Grupo | Acciones | Qué significa |
|---|---|---|
| Lectura / exfiltración | git.clone, git.fetch, repo.download_zip | El código (y todos los secretos de su historial) ha salido |
| Escritura / manipulación | git.push, workflows.created_workflow_run en una rama nueva, protected_branch.destroy | Se han modificado el código o los pipelines. Consulta fuga de secretos de Actions |
| Persistencia | public_key.create, hook.create, integration_installation.create, org.register_self_hosted_runner, personal_access_token.request_created | Acceso que sobrevive a la revocación |
Los ámbitos del token marcan los límites. Un token clásico con repo y workflow puede hacer push de ficheros de workflow. Con admin:org puede cambiar la configuración de la organización. Un token fine-grained solo alcanza los repositorios y permisos que se le concedieron y, en las organizaciones que exigen aprobación, los eventos personal_access_token.request_created y .access_granted muestran cuándo obtuvo ese acceso.
Elimina lo que dejó el token
La revocación no deshace lo que creó el atacante. Antes de cerrar el incidente:
- Borra las deploy keys y claves SSH desconocidas (
public_key.create). Una deploy key con permiso de escritura conserva el acceso de push tras cualquier cambio de contraseña o de token. - Elimina los webhooks, las GitHub Apps, las aprobaciones de OAuth apps y los runners self-hosted añadidos durante la ventana.
- Borra las ramas del atacante y revisa
.github/workflowsen todas las ramas, no solo en la predeterminada. - Rota los secretos de Actions que pueda leer cualquier workflow al que el token pudiera hacer push y, después, las claves cloud que haya entre ellos (consulta el salto al cloud).
- Busca otros tokens del mismo propietario usados desde la IP del atacante. Pivota sobre la IP, no solo sobre el hash.
Evita el siguiente
- Define una política de tokens de acceso personal en la organización: restringe los tokens clásicos, impón una duración máxima y exige aprobación para los tokens fine-grained. El analizador señala cuándo se debilita esa política (
pat-policy-weakened). - En Enterprise Cloud, una lista de IP permitidas se aplica también a los tokens de acceso personal y a las claves SSH. Así, los tokens robados solo funcionan desde tus redes.
- Para la automatización, prefiere GitHub Apps con tokens de instalación de corta duración a PAT de larga duración.
Preguntas frecuentes
¿Revoca GitHub automáticamente los tokens filtrados?
GitHub revoca automáticamente un token OAuth, un token de GitHub App o un token de acceso personal válido que se haya subido a un repositorio público o a un gist público. Los tokens filtrados en otros sitios (un repositorio privado, un log de CI, un chat, un volcado de un infostealer) no se revocan por ti.
¿Cómo encuentro los eventos de un token en el registro de auditoría?
Calcula su hash con echo -n TOKEN | openssl dgst -sha256 -binary | base64 y después busca en el registro de auditoría hashed_token:"VALOR". Ten en cuenta que las búsquedas desde la interfaz y la API excluyen los eventos Git. Expórtalos para comprobar los clonados.
¿Basta con revocar el token?
No. La revocación impide el uso futuro, pero no deshace lo que el atacante ya hizo o dejó instalado: las claves SSH y deploy keys, las OAuth apps, los workflows en ramas nuevas, los runners y los webhooks sobreviven a ella. Determina el alcance de la actividad del token, elimina la persistencia y rota los secretos que podían leer sus workflows.