Esta historia me puso a pensar: https://www.404media.co/fbi-extracts-suspects-deleted-signal-messages-saved-in-iphone-notification-database-2/
El FBI recuperó mensajes de Signal del teléfono de un sospechoso, el cifrado de Signal fue roto, nada es seguro, etc. Pero no es así. El cifrado de Signal no fue vulnerado. Lo que el FBI hizo en realidad fue extraer el contenido descifrado de los mensajes del almacenamiento interno de notificaciones de Apple en el iPhone del sospechoso, lo cual no tiene nada que ver con la criptografía de Signal y sí todo que ver con cómo los teléfonos manejan las push notifications.
Aunque este caso ocurrió en un iPhone, el problema no es exclusivo de iOS. Por defecto, tanto Android como iOS escriben los mensajes de notificaciones descifrados en bases de datos del sistema que persisten tras eliminar la app y que ignoran por completo la configuración de mensajes que desaparecen. Esos datos son accesibles para cualquiera con acceso físico a tu dispositivo.

Lo Que Realmente Ocurrió
En abril de 2026, 404 Media reportó el testimonio de un agente del FBI durante un juicio federal contra acusados por actividades en un centro de detención del ICE en Texas. Una de las acusadas tenía Signal instalado en su iPhone en el momento de los hechos. Para cuando los investigadores examinaron el teléfono, Signal ya había sido eliminado.
De todas formas recuperaron mensajes de Signal, específicamente los entrantes, a través del almacenamiento interno de notificaciones de Apple. La app ya no estaba, pero los mensajes sí.
Cómo Funciona Signal en iOS
El caso del FBI involucró un iPhone, así que veamos cómo iOS maneja el almacenamiento de push notifications y Signal en particular.
APNs: La Infraestructura Centralizada de Push de Apple
Apple Push Notification Service (APNs) es la única forma de entregar notificaciones en segundo plano a iPhones. Cada notificación remota de cada app debe pasar por los servidores de Apple antes de llegar al dispositivo.
Cuando una app se registra para push notifications, el dispositivo obtiene un APNs device token, un identificador único que Apple usa para enrutar las notificaciones a ese dispositivo específico. Los servidores de Signal guardan este token y lo usan para solicitar la entrega de notificaciones a través de Apple. A partir de ese momento, Apple es un intermediario obligatorio para cada notificación que Signal envía.
NSE: La Notification Service Extension
Signal en iOS usa una UNNotificationServiceExtension (NSE), un proceso aislado que iOS ejecuta entre la recepción de una notificación APNs y su presentación al usuario. Es como un middleware que intercepta la notificación antes de que aparezca en tu pantalla.
Así es como funciona el flujo:
- El servidor de Signal envía un payload APNs a Apple con 2 cosas: un flag
mutable-content: 1, que le indica a iOS que ejecute la NSE antes de mostrar nada, y un blob de texto cifrado con el contenido real del mensaje. Apple enruta este payload al dispositivo de destino. El contenido del mensaje (aunque cifrado), viaja a través de la infraestructura de Apple. - Una vez que el payload llega al dispositivo, iOS ejecuta la NSE de Signal en un proceso aislado separado para descifrar el texto localmente usando las claves del Signal Protocol y llama a
contentHandler()con el nombre del remitente y el texto del mensaje en texto plano y legible. - iOS toma ese output descifrado, lo almacena en la base de datos de notificaciones del teléfono en
/var/mobile/Library/UserNotifications/, y muestra la notificación.
Esa base de datos local es lo que el FBI extrajo. Persiste tras eliminar la app, tras la expiración de los mensajes, y tras cada temporizador de mensajes que desaparecen que hayas configurado.
Cómo Funciona Signal en Android
Android maneja esto de forma completamente diferente:
Lo Que Google Realmente Ve a Través de FCM
Android no permite que las apps mantengan conexiones persistentes en segundo plano con sus propios servidores. Las optimizaciones de batería, App Standby, y los límites de procesos en segundo plano lo impiden. En cambio, Google centraliza toda la entrega de push a través de una única conexión persistente gestionada por Google Mobile Services (GMS).
GMS mantiene un socket TCP siempre activo hacia los servidores de Firebase Cloud Messaging (FCM) que sobrevive a todos los estados de energía excepto cuando el dispositivo está apagado. Cada push notification de cada app pasa por este canal.
FCM soporta 2 tipos de mensajes:
- Notification messages llevan un título y cuerpo directamente en el payload de FCM. Android puede renderizar la notificación desde ese payload sin despertar la app. El contenido viaja a través de la infraestructura FCM de Google. Los servidores de Google llevan el texto de la notificación.
- Data messages son los que usa Signal. Solo llevan un payload personalizado de clave-valor y requieren que la app receptora lo maneje en código. El payload del data message de Signal es esencialmente vacío, un ping de alta prioridad para despertar la app sin contenido del mensaje en absoluto. El texto real de tu mensaje nunca está en el payload de FCM en ningún punto del tránsito.
Todo lo que la infraestructura de Google ve es un FCM registration token del dispositivo de destino, el identificador de la app de Signal (org.thoughtcrime.securesms), un flag de prioridad, y un timestamp. Nada más.
Este es el contraste con iOS. En iOS, la infraestructura APNs de Apple lleva un blob cifrado. En Android, el FCM de Google no lleva nada relacionado con el contenido de tu mensaje en ningún punto del tránsito, ni siquiera texto cifrado.
Lo que Google sí tiene son metadatos de entrega: el FCM registration token de tu dispositivo, que mapea a tu cuenta de Google, y un registro con timestamp de cuándo se entregó una notificación de Signal a tu dispositivo (lo cual es útil para rastrearte, así que…).
El Double Ratchet: Dónde Ocurre el Descifrado
Cuando GMS recibe el FCM data message y despierta el FirebaseMessagingService de Signal, Signal obtiene aproximadamente una ventana de ejecución de 20 segundos. Usa esa ventana para abrir una conexión TLS a los servidores propios de Signal y obtener el payload del mensaje cifrado.
El cifrado es el Signal Protocol, que combina 2 mecanismos:
- X3DH (Extended Triple Diffie-Hellman) maneja el acuerdo de claves inicial cuando 2 partes establecen una conversación por primera vez.
- El algoritmo Double Ratchet deriva una nueva clave de cifrado para cada mensaje individual usando tanto un symmetric-key ratchet como un Diffie-Hellman ratchet. Esto significa que los mensajes pasados permanecen cifrados incluso si una clave actual se ve comprometida, porque cada paso del ratchet genera una nueva clave y descarta la anterior. Los mensajes futuros también vuelven a ser seguros una vez que el ratchet avanza tras un compromiso.
Los servidores de Signal solo almacenan texto cifrado y no pueden descifrar nada. Signal no puede leer tus mensajes. El descifrado ocurre íntegramente en tu dispositivo, con claves privadas almacenadas en el hardware-backed Keystore de Android en dispositivos con secure element.
El Handoff: Donde el OS Toma el Control
Una vez que Signal descifra el mensaje y necesita mostrártelo, llama al NotificationManager de Android. Simplificado, esa llamada se ve así:
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(senderName)
.setContentText(messageBody)
.setSmallIcon(R.drawable.ic_notification)
.build()
NotificationManagerCompat.from(context).notify(notificationId, notification)
En el momento en que Signal llama a .notify(), el senderName y el messageBody salen del proceso aislado de Signal y se entregan a un servicio del sistema. El OS de Android toma posesión de ellos para renderizarlos: la pantalla de bloqueo, el panel de notificaciones, los dispositivos Wear OS conectados, y el logging a nivel de sistema.
Si configurás en Signal la opción de contenido de notificaciones a “No Name or Message”, lo que se pasa a .setContentTitle() y .setContentText() es un placeholder genérico. El OS toma posesión de ese placeholder, y nada sensible termina en ninguna base de datos.
Dónde Android Guarda Tus Notificaciones
El System Notification Log
Cuando Signal llama a NotificationManager.notify(), Android escribe el evento de notificación en una base de datos SQLite en /data/system/notification_log.db. Esta base de datos es propiedad del OS, no de Signal. Signal no tiene visibilidad sobre ella y no tiene forma de eliminar entradas de ella. Desde la perspectiva de Signal, no sabe que la base de datos existe.
El schema almacena: pkg (el nombre del paquete de la app que publica, ej. org.thoughtcrime.securesms), uid, when (timestamp Unix en milisegundos), tag, key, y los campos de contenido con los strings que Signal pasó a .setContentTitle() y .setContentText() al llamar a notify(). Si esos strings eran el nombre del remitente y el cuerpo del mensaje, eso es exactamente lo que contiene la base de datos.
Esta base de datos es invisible para los usuarios. No hay ningún menú de Settings que la exponga y no hay forma de limpiarla manualmente a través de la interfaz de Android. Acceder a ella requiere extracción física del dispositivo mediante herramientas forenses.
Las Notificaciones Eliminadas No Desaparecen Realmente
El comportamiento de eliminación de SQLite es la razón por la que esta base de datos funciona como un artefacto forense.
Cuando SQLite elimina una fila, no sobreescribe los bytes en el disco. Marca las páginas que contienen esa fila como libres y disponibles para reutilización. Los datos permanecen ahí hasta que SQLite decide asignar esas páginas para nuevas escrituras.
Las herramientas forenses parsean la base de datos a nivel de página, leyendo las páginas “desasignadas” para recuperar los datos de filas eliminadas. Este contenido típicamente permanece recuperable durante semanas o más, hasta que suficientes escrituras nuevas lo desplazan físicamente.
El FCM Queued Messages Store
Por separado, los mensajes FCM no entregados se ponen en cola en una base de datos LevelDB en /data/data/com.google.android.gms/databases/fcm_queued_messages.ldb/, usada por Play Services para almacenar los payloads de FCM que llegaron mientras la app no estaba en ejecución.
Para la mayoría de las apps que usan FCM de tipo notification, este store puede contener contenido real de notificaciones. Para Signal, es menos interesante desde el punto de vista forense. Como Signal usa FCM data-only messages con payloads vacíos, el queued message store no contiene más que pings de activación y timestamps de entrega. Metadatos útiles (que siendo honesto, hay gente que ha sido asesinada solo por metadatos), pero no contenido de mensajes.

Qué Deberías Hacer
Lo que debés hacer depende de tu nivel de paranoia y cuánto querés complicarte la vida. Lo siguiente se enfoca en Android porque es lo que más manejo.

1. Cambiá la Configuración de Notificaciones de Signal
- Abrí Signal -> Settings -> Notifications -> Show, y configuralo en “No Name or Message”
- Esto controla qué strings le pasa Signal a
NotificationManager, lo que determina qué termina ennotification_log.db. Con este cambio, el contenido de los mensajes nunca se escribe en la base de datos de notificaciones.
2. Eliminá los Permisos de Notificación de Signal por Completo
- Abrí Android Settings -> Apps -> Signal -> Notifications -> Off.
- Esto elimina los permisos de notificación de Signal a nivel del OS, lo que hace que GMS cancele el registro del FCM token de Signal y lo elimine de los registros de Google.
- Signal cae de nuevo a WebSocket polling. Los mensajes llegarán en segundos o hasta un minuto en lugar de instantáneamente, pero esto también elimina por completo el vector de exposición de FCM.
3. Usá una VPN
- Siempre enrutá tu tráfico a través de una VPN de confianza.
- Cualquier servidor al que se conecte tu dispositivo (incluidos los servidores FCM de Google) verá la IP del endpoint de la VPN en lugar de la tuya real.
- Eliminar los permisos de notificación de Signal como se describe arriba también elimina este vector de exposición, pero una VPN agrega una capa de protección para todo lo demás que corre en tu dispositivo.
4. Usá GrapheneOS
- Usá GrapheneOS para ir un paso más allá. GrapheneOS es un fork de Android hardened construido para hardware Google Pixel que corre sin ningún token ni infraestructura de Google. Sin GMS, sin FCM, sin pings a los servidores de Google.
- Signal en GrapheneOS puede usar su propia entrega de push basada en WebSocket sin ninguna participación de Google, lo que elimina esta preocupación por completo.
Tené en cuenta que todo cambia constantemente, pero este post debería darte una idea de qué hacer no solo con Signal, sino con cualquier otra app. En cuanto al tema en cuestión, el cifrado de Signal no es el eslabón débil. El eslabón débil es la infraestructura en la que tu OS se apoya para mostrarte una notificación y lo que hace con ese contenido después.