Un inicio de sesión SSH exitoso sin intentos fallidos previos puede parecer inofensivo, pero es una señal que merece revisión. En este artículo repasamos, paso a paso, cómo investigar un acceso así y cómo evitar que se repita, basándonos en un incidente real: una clave SSH de un servidor de compilación retirado seguía autorizada en producción.
El escenario
Un servidor Linux de producción registró dos inicios de sesión SSH exitosos desde una IP externa durante la noche. Lo llamativo: no hubo intentos fallidos de contraseña antes. Eso descarta un ataque de fuerza bruta y apunta a que el acceso se hizo con credenciales válidas.
Paso 1: Identificar el método de autenticación
Revisa los registros de SSH y busca las líneas de inicio de sesión exitoso. Según tu sistema, estarán en uno de estos lugares:
- Journal de systemd (por defecto en distribuciones recientes como Debian 13):
journalctl -u ssh | grep Accepted
En algunos sistemas el servicio se llama sshd; si el comando no devuelve nada, prueba con -u sshd.
- Archivos de log tradicionales:
/var/log/auth.log(Debian/Ubuntu conrsyslog) o/var/log/secure(RHEL/CentOS).
Si ves esto:
Accepted publickey
significa que el acceso fue con clave pública, no con contraseña. Quien entró tenía la clave privada correspondiente a una clave autorizada en el servidor.
Paso 2: Obtener la huella de la clave usada
La línea Accepted publickey incluye la huella digital (fingerprint) de la clave con la que se autenticó la sesión, con un formato similar a este:
Accepted publickey for deploy from <IP> port <PUERTO> ssh2: ED25519 SHA256:xxxxxxxxxxxx
Anota la huella (SHA256:...), la IP de origen y la hora.
Paso 3: Compararla con las claves autorizadas
Revisa el archivo de claves autorizadas del usuario con el que se inició sesión:
/home/deploy/.ssh/authorized_keys
Para ver la huella de cada clave del archivo:
ssh-keygen -lf /home/deploy/.ssh/authorized_keys
Compara cada huella con la que sacaste del registro. En nuestro caso había una sola clave, y coincidía.
Paso 4: Revisar el comentario de la clave
El comentario al final de cada clave suele indicar su origen. Aquí era:
deploy@build-prerelease
Búscalo en tu inventario de servidores y registros internos. En nuestro caso, ese servidor de compilación había sido retirado meses atrás, pero su clave nunca se eliminó del servidor de producción.
Ojo: el comentario es solo texto y puede editarse. Úsalo como pista, pero confía en la huella para la verificación.
Paso 5: Determinar desde cuándo existe la clave
Revisa la marca de tiempo del archivo:
stat /home/deploy/.ssh/authorized_keys
La clave llevaba ahí desde octubre de 2025. Esto indica que no era una persistencia recién plantada por el atacante, sino una relación de confianza antigua que nadie revisó. Ten en cuenta que la fecha de modificación refleja el último cambio del archivo completo, no necesariamente cuándo se añadió una clave en particular.
Paso 6: Confirmar el origen de las sesiones
Revisa los accesos exitosos dentro de la ventana de tiempo sospechosa y verifica si ambas sesiones vinieron de la misma IP:
journalctl -u ssh --since "AAAA-MM-DD HH:MM" --until "AAAA-MM-DD HH:MM" | grep -E "Accepted|session opened"
Esto te da la IP de origen, la hora y el método de autenticación de cada sesión, y las líneas session opened / session closed te permiten estimar su duración.
Nota sobre
last: en distribuciones recientes como Debian 13 el comandolastya no viene incluido (desde util-linux 2.40 se separó en el paquetewtmpdb). Puedes instalarlo conapt install wtmpdby usarwtmpdb last, pero solo registrará sesiones a partir de su instalación, así que no sirve para investigar un incidente pasado. Para eso, usa el journal oauth.log.
Paso 7: Buscar qué se tocó durante las sesiones
Busca archivos creados o modificados en la ventana de tiempo de las sesiones:
find /opt /home -type f -newermt "AAAA-MM-DD HH:MM" ! -newermt "AAAA-MM-DD HH:MM"
Ajusta las fechas y los directorios a tu caso.
En el incidente apareció una copia de seguridad del archivo .env de la aplicación en /opt/app/. Los archivos de entorno suelen contener credenciales de base de datos, claves de API y secretos de la aplicación, así que hay que asumir que quedaron expuestos.
Paso 8: Contener y remediar
Con la evidencia en mano, estas fueron las acciones:
- Desactivar la cuenta afectada (
deploy). - Eliminar la clave obsoleta de
authorized_keys. - Rotar los secretos de la aplicación: credenciales de base de datos, claves de API y cualquier otro valor del
.env. - Auditar el resto de la infraestructura en busca de otros servidores que confíen en la misma clave.
Para el punto 4, una búsqueda por huella en cada servidor ayuda a encontrar rastros de la misma clave:
ssh-keygen -lf /home/USUARIO/.ssh/authorized_keys
Repítelo para cada usuario con acceso SSH y compara con la huella que identificaste.
Cómo prevenirlo
- Inventaría tus claves SSH: documenta qué clave pertenece a qué persona, servidor o servicio.
- Incluye la revisión de claves en el proceso de baja: cuando retires un servidor, un empleado o un servicio, elimina sus claves de todos los sistemas donde estaban autorizadas.
- Audita authorized_keys periódicamente: revisa que cada clave tenga un dueño y un propósito vigentes.
- Usa claves con alcance limitado: en authorized_keys puedes restringir una clave con opciones como from=»IP» o command=»…», para que aunque se filtre tenga un alcance reducido.
- Considera certificados SSH con caducidad: a diferencia de las claves estáticas, expiran solos.
- Monitorea inicios de sesión inusuales: configura alertas por accesos desde IPs nuevas, aunque la autenticación sea válida.
Descuidos que se pueden aprovechar
No todo compromiso empieza con malware ni con una contraseña débil. A veces el atacante solo necesita una credencial que tu entorno olvidó que existía. Las contraseñas expiran, los empleados se van y los servidores se retiran, pero una clave SSH puede permanecer en authorized_keys durante años si nadie la revisa.
Auditar y rotar claves no es glamuroso, pero es una de las defensas más baratas y efectivas que puedes tener.
Tema Relacionado: Infraestructura Linux/VPS