Cómo APT verifica paquetes: La cadena de confianza de Debian

Publicado por Diego Córdoba el

Hoy analizaremos la cadena de confianza de Debian y derivadas en la gestión de paquetes y repositorios, y cómo APT verifica paquetes que se instalan.

¿Alguna vez te preguntaste cómo sabe APT que el software que estás instalando es realmente el que deberías instalar? ¿Cómo puede estar seguro de que nadie manipuló el paquete en el camino, ni en el servidor, ni en tu conexión, ni en ningún espejo intermedio?

La respuesta corta es: firmas digitales y una cadena de verificaciones.

La respuesta larga es la que vamos a desarrollar acá, paso a paso, con comandos para que podás verlo y entender los detalles.

APT verifica paquetes

El problema que resuelve APT

Supongamos que queremos instalar el paquete bash. Lo que hace APT es:

  1. Descargar el archivo .deb desde un servidor espejo (mirror)
  2. Desempaqueta el .deb.
  3. Instalar en tu sistema con privilegios de root

Ahora, la pregunta del millón:

  • ¿Cómo sabe APT que ese .deb es el que el mantenedor de Debian subió?
  • ¿Qué pasa si un atacante modifica el paquete en el servidor?
  • ¿Qué pasa si un espejo está comprometido?
  • ¿Qué pasa si alguien intercepta tu tráfico y te envía un paquete malicioso?

La respuesta de Debian (y de cualquier distribución Linux seria) es: firmas digitales y una cadena de confianza.

La cadena de confianza de APT (el concepto)

APT construye una cadena de verificaciones que va desde un archivo firmado hasta el paquete individual:

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   InRelease (firmado por GPG)                               │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ SHA256: 819..   565.. main/binary-amd64/Packages    │   │
│   │ SHA256: 044..   133.. main/binary-amd64/Packages.gz │   │
│   └─────────────────────────────────────────────────────┘   │
│                         │                                   │
│                         │ contiene el HASH de               │
│                         ▼                                   │
│   ┌─────────────────────────────────────────────────────┐   │
│   │  Packages                                           │   │
│   │  ┌─────────────────────────────────────────────────┐│   │
│   │  │ Package: bash                                   ││   │
│   │  │ Version: 5.2.37-2+b9                            ││   │
│   │  │ SHA256: 03571d...  bash_5.2.37-2+b9_amd64.deb   ││   │
│   │  └─────────────────────────────────────────────────┘│   │
│   │  ┌─────────────────────────────────────────────────┐│   │
│   │  │ Package: openssl                                ││   │
│   │  │ Version: 3.5.6-1                                ││   │
│   │  │ SHA256: a1b2c3...  openssl_3.5.6-1_amd64.deb    ││   │
│   │  └─────────────────────────────────────────────────┘│   │
│   └─────────────────────────────────────────────────────┘   │
│                         │                                   │
│                         │ contiene el HASH de               │
│                         ▼                                   │
│   ┌─────────────────────────────────────────────────────┐   │
│   │  bash_5.2.37-2+b9_amd64.deb                         │   │
│   │  (el paquete real)                                  │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Cada eslabón verifica al siguiente. Si alguno falla, APT se niega a instalar el paquete.

  • ¿Cómo sabe APT que el paquete .deb es original? -> Porque calcula el hash del paquete .deb descargado, y lo compara con el hash que se encuentra guardado dentro del archivo Packages del repositorio.
  • ¿Cómo sabe que el archivo Packages del repositorio original? -> Porque descarga el Packages.gz, lo descomprime, calcula el hash del archivo, y lo compara con el que se encuentra dentro de InRelease.
  • ¿Y cómo sabe que el archivo InRelease es original? -> Porque está firmado con la llave pública del equipo de maintainers de la distro, almacenada en el keyring local.
  • ¿Y cómo sabe que la llave pública es realmente de los maintainers? -> Porque está en nuestro sistema desde que lo instalamos, y confiamos en ella 🙂

¿Dónde vive todo esto en el disco?

Cuando ejecutamos sudo apt update, APT descarga los archivos de metadatos del repositorio y los guarda en /var/lib/apt/lists/.

Si entramos ahí, vamos a ver archivos con nombres como:

deb.debian.org_debian_dists_bookworm_InRelease
deb.debian.org_debian_dists_bookworm_main_binary-amd64_Packages
deb.debian.org_debian_dists_bookworm_main_binary-amd64_Packages.gz

Estos son los archivos que APT usa para verificar todo. La mayoría son archivos de texto plano, así que podemos leerlos y entender qué está pasando.

El archivo Packages y los hashes

El archivo Packages de cada repo es un índice enorme que lista todos los paquetes del repositorio. Para cada paquete, incluye:

  • Nombre
  • Versión
  • Dependencias
  • Filename (dónde encontrar el .deb)
  • SHA256 (el hash criptográfico del .deb)

Veamos un ejemplo real para el paquete bash:

cat /var/lib/apt/lists/deb.debian.org_debian_dists_trixie_main_binary-amd64_Packages | awk '/^Package: bash$/,/^$/' | grep -E "^(Package|Version|Filename|SHA256)"

Salida típica:

Package: bash
Version: 5.2.37-2+b9
Filename: pool/main/b/bash/bash_5.2.37-2+b9_amd64.deb
SHA256: 03571d298ed24c615641d54dec4eb023ef5710a2b8cb9304acb66e372cab9c46

Ese SHA256 es el hash del archivo .deb. Cuando APT descarga el paquete, calcula su hash y lo compara con este valor. Si no coinciden → algo anda mal y la instalación se cancela.

El archivo Package.gz es una versión comprimida con Gzip del archivo Package del repositorio.

El archivo InRelease: el manifiesto firmado

El archivo InRelease es el manifiesto del repositorio. Contiene los hashes de, entre otros, todos los archivos Packages y Packages.gz del repositorio.

Para verlo:

cat /var/lib/apt/lists/deb.debian.org_debian_dists_trixie_InRelease |grep Packages

Salida típica:

[...]
 68ef2989627ff3e182f31728778985724f6d8d8cfef7d43a96b1b18491407371  6099592 main/binary-all/Packages.gz
 40daf3e3a7a556cf70891b4c027d60096ce5eba55b4f36ed3ac03e0cdcd0feff  4531308 main/binary-all/Packages.xz
 81953f092313933b22d2c08538def6d4504e18492853b45917cf057df55034de 56547292 main/binary-amd64/Packages
 044d6ab09e4f11d1808acd886f267e1769fd4f1438672bf055f8daeebdec7d95 13322415 main/binary-amd64/Packages.gz
 3a66223535ccb4af69b6bd323a8d8cd46a665a00622ef1a63321ca71ddb2b092  9671288 main/binary-amd64/Packages.xz
 654bfc44b149620ce2bdc019f198625e838d0df39b8a370512517c76636a013b 56090107 main/binary-arm64/Packages
 317123a6f8f45774abdd4612e16f8032e6f043cfd0cb1e36ffa1c7a31d46c105 13232027 main/binary-arm64/Packages.gz
 5d4a0cc45158693a0fba0863f63727925018d3c52808de3c5bbc9915f0189d31  9608036 main/binary-arm64/Packages.xz
 5f1badc3e9d1b035f33f1889f8ce98cbfa0e9afae1db559bfef140180046aaeb 52812883 main/binary-armel/Packages
 967a690a870a5086b3ee7f8f80083327d93ac512260c57f55c9540aa2053acbb 12607946 main/binary-armel/Packages.gz
 20f0ffecbf3baa12bfd48a2b790bf2941f2cb334e80631f0c22042caea0d64b8  9164072 main/binary-armel/Packages.xz
 296c5b1c9d2c6b96dcbf12071e1aa95e288d2d4ee50ee2299f6b0de78a28a569 53312532 main/binary-armhf/Packages
 36a0dee5fcac24bf0543032bd3d9657975f3c062d29a42ce15636d3e77bf7433 12718011 main/binary-armhf/Packages.gz
[...]

Esto significa, si analizamos algún hash listado:

  • El archivo main/binary-amd64/Packages tiene que tener el hash 81953f...
  • El archivo main/binary-amd64/Packages.gz (comprimido) tiene que tener el hash 044d6ab09e...

Cuando ejecutamos apt update, APT descarga los archivos Package.gz, los descomprime, calcula su hash, y verifica que coincida con lo que dice el archivo InRelease. Si no coinciden → algo anda mal.

La firma GPG de InRelease (la raíz de la confianza)

El archivo InRelease, a su vez, está firmado digitalmente por el equipo de mantainers de Debian.

Podemos verificar la firma manualmente con gpg:

gpg --verify --keyring /usr/share/keyrings/debian-archive-keyring.gpg /var/lib/apt/lists/deb.debian.org_debian_dists_trixie_InRelease

Si la firma es válida, vas a ver algo como:

Good signature from "Debian Archive Automatic Signing Key (11/bullseye)"

Si la firma falla, vas a ver un error claro. Y APT no te va a dejar instalar nada hasta que resuelvas el problema.

Esta es la raíz de la cadena de confianza: si la firma del Release es válida, y los hashes coinciden en cadena, entonces los .deb son legítimos.

¿Por qué el WARNING?

Cuando verificamos firmas de paquetes:

  1. Descargamos la clave pública del repositorio desde Internet
  2. Verificamos la firma matemáticamente
  3. GPG te dice: «La firma es válida, pero yo no sé si vos confiás en el dueño de esta clave»

Eso es normal. Confiamos en Debian por decisión propia, no porque alguien más te lo haya certificado mediante la Web of Trust.

¿Dónde se guardan las llaves públicas?

Las llaves de los repositorios se guardan en estos lugares:

UbicaciónPropósitoEstado
/usr/share/keyrings/Llaves de repositorios oficiales de Debian/Ubuntu✅ Actual
/etc/apt/keyrings/Llaves de repositorios de terceros✅ Recomendado (moderno)
/etc/apt/trusted.gpg.d/Llaves «globales» (cualquier repositorio)Deprecated / obsoleto

La práctica moderna y segura es aislar cada llave en su propio archivo y vincularla a un repositorio específico con la opción signed-by dentro del archivo de configuración del repo, ya sea en formato one-line o Deb822.

Ejemplo de configuración segura en /etc/apt/sources.list.d/mirepo.list:

# En formato one-line:
deb [signed-by=/etc/apt/keyrings/mirepo.gpg] https://mirepo.com/debian stable main

# En formato deb822:
Types: deb
URIs: https://......
Suites: trixie
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/mirepo.gpg

Así, si una llave de un repositorio de terceros se ve comprometida, el daño queda limitado a ese repositorio.


Bonus: Inspeccionar un .deb descargado

Aunque el .deb individual no contiene la firma GPG (la firma está en InRelease), sí podemos inspeccionar su contenido:

# Descargar un paquete sin instalarlo
apt download bash

# Inspeccionar los metadatos internos
dpkg --info bash_*.deb

Dentro del .deb vamos a encontrar:

  • Los archivos que se instalan
  • Los scripts de control (preinst, postinst, etc.)
  • Los hashes internos de sus propios archivos (usados por dpkg para verificar integridad)

Pero la confianza sobre el origen del .deb no está dentro del paquete. Está en la cadena de hashes y firmas que comienza en el archivo InRelease.

Secuencia de la cadena de confianza

                        [ apt update ]
                             │
                             │
                             ▼
                    ┌─────────────────────┐
                    │  Descarga InRelease │
                    │  (archivo firmado)  │
                    └──────────┬──────────┘
                               │
                               ▼ gpg --verify
                    ┌─────────────────────┐
                    │  Llave pública de   │
                    │  Debian en keyring  │
                    └──────────┬──────────┘
                               │
                               │ Si firma OK
                               ▼
                    ┌─────────────────────┐
                    │  Lee hashes SHA256  │
                    │  de Packages.gz     │
                    └──────────┬──────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │  Descarga Packages  │
                    │  Verifica su hash   │
                    └──────────┬──────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │  Lee hash SHA256    │
                    │  del paquete .deb   │
                    └──────────┬──────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │  Descarga el .deb   │
                    │  Verifica su hash   │
                    └──────────┬──────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │   dpkg -i instala   │
                    └─────────────────────┘

Cada flecha es una verificación. Si alguna falla, la instalación se detiene.


Conclusión de cómo APT verifica paquetes

Lo bueno: APT no confía ciegamente en ningún paquete ni en ningún servidor.

Construye una cadena de verificaciones que arranca en una firma GPG (raíz de confianza) y termina en el hash SHA256 del .deb que descargamos.

  • La firma GPG está en el archivo InRelease del sistema, no dentro del .deb.
  • Los hashes SHA256 unen cada eslabón de la cadena de confianza.
  • Podemos inspeccionar manualmente cada paso mirando los archivos en /var/lib/apt/lists/
  • La práctica moderna de llaves es: una llave por repositorio, usando signed-by en el archivo .list (formato Deb822).

Este sistema de seguridad es transparente y verificable. No es una «caja negra». Cualquier usuario con curiosidad puede abrir los archivos, ver los hashes, verificar las firmas y convencerse de que el paquete que va a instalar es legítimo.

Espero que se haya entendido!

Esto va a ser publicado también en nuestro canal de youtube, no olviden suscribirse para enterarse!


🔗 Recursos adicionales


¿Necesitas que ajuste algo del tono, la longitud o algún comando específico antes de que lo publiques?


¿Querés aprender más? 📚

👉 Visitá nuestros cursos!
💬 Y si tenés dudas, o querés dejarnos tus comentarios sumate a la Comunidad JuncoTIC en Telegram!
¡Te esperamos!

Categorías: Sin categoría

Diego Córdoba

- Ingeniero en Informática - Mgtr. Teleinformática - Instructor GNU/Linux, Programación, Redes TCP/IP, criptografía y ciberseguridad. - (Ex) Docente universitario e investigador