Cada vez que nos conectamos a través del protocolo ssh al servidor, el cliente ssh verifica si la clave pública de este servidor coincide con la que fue la última vez (al menos recomienda hacer el estándar ssh). En OpenSSH, una lista de claves de servidor conocidas se almacena en el archivo known_hosts. Bajo katom brevemente sobre qué y cómo se almacena exactamente allí.
Todos los experimentos se realizaron en Linux (Debian / Mint / Ubuntu). No puedo garantizar la ubicación y el contenido de los archivos en otros sistemas operativos.Cuando nos conectamos al servidor ssh por primera vez, vemos algo como esto:
No se puede establecer la autenticidad del host '192.168.0.2 (192.168.0.2)'.
La huella digital de la clave RSA es SHA256: kd9mRkEGLo + RBBNpxKp7mInocF3 / Yl / 0fXRsGJ2JfYg.
¿Estás seguro de que deseas continuar conectando (sí / no)?
Si está de acuerdo, la siguiente línea se agregará al archivo ~ / .ssh / known_hosts:
| 1 | CuXixZ + EWfgz40wpkMugPHPalyk = | KNoVhur7z5NAZmNndtwWq0kN1SQ = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh / e4q91 + iz + + i9S0s3M2LPq GAhRlhKt5vKyEVd6x6m26cc98Y + + SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x mPvO9FLSBk / Al2GbH5q6F + + hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti + + 7xGwT8DF tIyLFcU + zxd0QnwJIbNvewkHs0LsMOWFVPz / Nd0XiVXimX + ugCDBZ / 4q8NUwH9SGzCMAvnnr + D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV
Aquí, tres elementos se escriben con un espacio: un hash en nombre del servidor, el nombre del algoritmo asimétrico utilizado y la clave pública del servidor. Vamos a desarmarlos.
Y si lees las instruccionesDe hecho, según el
manual de Ubunt, puede haber 2 campos más, también separados por espacios:
- al principio de la línea puede haber una nota "@ cert-Authority" o "@revoked", lo que significa, respectivamente, que la clave pública de la CA está escrita en esta línea o que esta clave ha sido revocada y no se puede utilizar.
- puede haber un comentario arbitrario al final de la línea
Nombre del servidor
En el ejemplo, el hash en nombre del servidor (host) se ve así:
| 1 | CuXixZ + EWfgz40wpkMugPHPalyk = | KNoVhur7z5NAZmNndtwWq0kN1SQ =
De hecho, el nombre de host en texto claro o una máscara que especifica el conjunto de nombres válidos se puede escribir aquí. Pero mi nombre de hash predeterminado se guarda. El registro se divide en 3 partes con el símbolo "|". La primera parte es el algoritmo hash. "1" corresponde a HMAC-SHA1 (no he visto otros). La segunda parte es la sal (clave para HMAC). La tercera parte es el propio hash (salida HMAC).
Chequefrom base64 import b64decode import hmac salt = b64decode("CuXixZ+EWfgz40wpkMugPHPalyk=") host = b'192.168.0.2' hash = hmac.HMAC(salt, host, 'sha1').digest() print(b64encode(hash).decode())
> 'KNoVhur7z5NAZmNndtwWq0kN1SQ ='
Algoritmo asimétrico
RFC-4253 enumera 4 algoritmos asimétricos: ssh-dss (obligatorio por estándar, pero considerado débil y desactivado por defecto desde OpenSSH7.0), ssh-rsa (recomendado), pgp-sign-rsa (opcional), pgp- sign-dss (opcional). Por defecto, las claves de los dos primeros tipos se generan en Linux para algoritmos de curva elíptica no mencionados en el RFC. Se prefiere este último, pero el cliente puede seleccionar el algoritmo con la opción HostKeyAlgorithms.
Cómo verificar la huella digital de la tecla deseada (no la predeterminada)Esto puede ser útil si, por ejemplo, cuando ingresa por primera vez al servidor desea verificar la huella digital de la clave y solo conoce la huella digital de la clave ssh-rsa. Entonces puedes conectarte con este comando:
ssh root@192.168.0.2 -o HostKeyAlgorithms = ssh-rsa
Si también necesita especificar un algoritmo de hash clave, puede usar la opción FingerprintHash. Por ejemplo, si solo se conoce md5 de ssh-rsa, puede conectarse así:
ssh root@192.168.0.2 -o HostKeyAlgorithms = ssh-rsa -o FingerprintHash = md5
Clave pública
La clave pública en known_hosts es la misma que la registrada en el archivo /etc/ssh/ssh_host_rsa_key.pub en el servidor (sustituya el nombre del algoritmo utilizado en lugar de rsa). Si elimina la codificación Base64, en el interior aparecerá nuevamente el nombre del algoritmo y los componentes clave reales.
¿Por qué no eliminar Base64? b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9e\x88^\x0e8\xe5!Z\xf3\x98\xae\x1f\xde\xe2\xafu\xfa,\xfe\x8b\xd4\xb4\xb3s6,\xfa\xbe\x18\x08Q\x96\x12\xad\xe6\xf2\xb2\x11Wz\xc7\xa9\xb6\xe9\xc7=\xf1\x8f\x92Ay\xc2\x07\xd1\x96yV$\xf29E\x1cE\xe7c\x86\x16yb\xc3\xc1r!\x1c\x12X\xb7\x9c\xb3\xde>V\x92\x0fy\xc7\xe9\x8f\xbc\xefE- d\xfc\tv\x19\xb1\xf9\xab\xa1~\x85\x92%.c\xbar"\x12\x99~\x13\xb5\xc1\xb5\xb3\x0e\x12\xc2\x84\xc0\x0e\xba\xe1t\xcd\x0e%\xdf\x9c\xe4%<\x8a\xa0\x9bs\xa3\xf3\x9d\x86\xcbep\xaf\xa8\xf6SY\xe4\x9bL\xb5\x1cR\xedS\x8b\xee\xf1\x1b\x04\xfc\x0c_\xad#"\xc5qO\xb3\xc5\xdd\x10\x9f\x02Hl\xdb\xde\xc2A\xec\xd0\xbb\x0c9aU??\xcdwE\xe2Ux\xa6_\xeb\xa0\x080Y\xff\x8a\xbc5L\x07\xf5!\xb3\x08\xc0/\x9ez\xfe\x0fR<_k\xe1J\xe4lN\xc4\x17/\x93\xf7\xbd\xff\x1e\x94<Ot:\xcc\'3m\x94\x10\x9b-l\xd5'
Se puede ver que hay 4 bytes en los que se escribe la longitud del campo, luego el campo en sí, etc. El primer campo es el nombre del algoritmo, el resto depende del algoritmo particular. En la clave anterior, 3 campos:
b'ssh-rsa' - b'\x01\x00\x01' - b'\x00\x9e\x88^\x0e8\xe5!Z\xf3\x98\xae\x1f\xde\xe2\xafu\xfa,\xfe\x8b\xd4\xb4\xb3s6,\xfa\xbe\x18\x08Q\x96\x12\xad\xe6\xf2\xb2\x11Wz\xc7\xa9\xb6\xe9\xc7=\xf1\x8f\x92Ay\xc2\x07\xd1\x96yV$\xf29E\x1cE\xe7c\x86\x16yb\xc3\xc1r!\x1c\x12X\xb7\x9c\xb3\xde>V\x92\x0fy\xc7\xe9\x8f\xbc\xefE- d\xfc\tv\x19\xb1\xf9\xab\xa1~\x85\x92%.c\xbar"\x12\x99~\x13\xb5\xc1\xb5\xb3\x0e\x12\xc2\x84\xc0\x0e\xba\xe1t\xcd\x0e%\xdf\x9c\xe4%<\x8a\xa0\x9bs\xa3\xf3\x9d\x86\xcbep\xaf\xa8\xf6SY\xe4\x9bL\xb5\x1cR\xedS\x8b\xee\xf1\x1b\x04\xfc\x0c_\xad#"\xc5qO\xb3\xc5\xdd\x10\x9f\x02Hl\xdb\xde\xc2A\xec\xd0\xbb\x0c9aU??\xcdwE\xe2Ux\xa6_\xeb\xa0\x080Y\xff\x8a\xbc5L\x07\xf5!\xb3\x08\xc0/\x9ez\xfe\x0fR<_k\xe1J\xe4lN\xc4\x17/\x93\xf7\xbd\xff\x1e\x94<Ot:\xcc\'3m\x94\x10\x9b-l\xd5' - N (0x101 * 8 = 2048 )
Huella digital
La huella digital de la clave que se propone verificar en la primera conexión es el hash correspondiente (en el ejemplo, SHA256) de la clave pública del último párrafo y de /etc/ssh/ssh_host_rsa_key.pub codificado en base64 para el hash de las funciones de la familia SHA o en hexadecimal para MD5.
Consideramos from hashlib import sha256 from base64 import b64decode, b64encode pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV") hash = sha256(pub_key_bin).digest() fingerprint = b64encode(hash) print(fingerprint) > b'kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='
+ iz + i9S0s3M2LPq + GAhRlhKt5vKyEVd6x6m26cc98Y + SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x + mPvO9FLSBk / Al2GbH5q6F + hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd + c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti + 7xGwT8DF + tIyLFcU + zxd0QnwJIbNvewkHs0LsMOWFVPz / Nd0XiVXimX + ugCDBZ / 4q8NUwH9SGzCMAvnnr + D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV") from hashlib import sha256 from base64 import b64decode, b64encode pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV") hash = sha256(pub_key_bin).digest() fingerprint = b64encode(hash) print(fingerprint) > b'kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='
Vemos que el hash realmente coincide con la huella digital que se muestra durante la primera conexión (cita al comienzo del artículo), hasta el símbolo "=" al final.
Aquí hay un pequeño programa para encontrar hosts en el archivo known_hosts, que apareció durante los experimentos.