Guía profunda y práctica sobre cifrar claves privadas en reposo en una pasarela de pagos cripto: el modelo de amenaza, brechas reales de la industria, AES-256-GCM y cifrado autenticado, envelope encryption y gestión de claves, y el proceso exacto sin downtime con el que TronDealer cifró más de 7.800 claves de wallets.
Imagina el peor martes de tu vida. Un snapshot de respaldo de tu base de datos de producción termina donde no debería: un bucket de S3 dejado público, una credencial de servicio filtrada, un portátil comprometido, un contratista curioso. Si esa base de datos guardaba las claves privadas de las wallets de tus clientes en texto plano, el atacante no necesita romper nada más. Ya tiene las claves. Cada dirección de depósito que generaste es suya para vaciar, en cada cadena, simultáneamente, en minutos.
Ese único escenario es la razón por la que el cifrado en reposo no es una casilla "deseable" para una pasarela de pagos cripto: es la diferencia entre un mal día y un evento de extinción. Esta guía recorre el panorama completo: cómo se filtra realmente el material criptográfico, la criptografía que lo protege, y el proceso exacto y sin downtime con el que en TronDealer ciframos más de 7.800 claves de wallets vivas en siete blockchains sin perder un solo pago.
La mayoría de las brechas son sobrevivibles: rotas contraseñas, invalidas sesiones, notificas a los usuarios. Una fuga de clave privada no lo es. On-chain no existe el "restablecer contraseña". Quien tiene la clave es la wallet. El cifrado en reposo es lo que convierte un compromiso de la base de datos de nuevo en un incidente sobrevivible.
Muchos desarrolladores asumen que como su sitio corre sobre HTTPS, sus datos están "cifrados". HTTPS (TLS) protege los datos en tránsito — mientras viajan entre el navegador y el servidor. No hace nada por los datos que descansan en un disco.
El modelo de amenaza de "en reposo" es el que la gente subestima. No estás defendiéndote de un hacker tecleando en tiempo real. Te defiendes de una copia estática de tus datos cayendo en manos equivocadas, donde el atacante tiene tiempo ilimitado y ningún límite de tasa.
El cifrado en reposo se gana su lugar porque los vectores de fuga son numerosos, mundanos y en su mayoría no tienen nada que ver con que tu código sea "hackeado":
axios con un troyano falso plain-crypto-js (marzo) y el gusano autopropagante ChainDrop que envenenó 444 paquetes empezando por keyv (agosto) — recolectaron específicamente tokens de .npmrc, credenciales de nube y wallets cripto de las máquinas de build. Tu propio código puede estar impecable y aun así ser el vehículo de entrega.Fíjate en el patrón: en casi todos los casos el atacante obtiene una copia de lectura de la base de datos, no acceso de shell a tu app en ejecución. El cifrado en reposo es precisamente el control que vuelve inútil esa copia.
Asume que tu base de datos se filtrará algún día — por un respaldo, una credencial o una dependencia que no controlas. Diseña de modo que cuando ocurra, el atacante obtenga ciphertext, no dinero. Esa asunción es toda la filosofía detrás del cifrado en reposo.
No todo cifrado es igual. Guardar claves "cifradas" con el algoritmo equivocado o los parámetros equivocados puede ser apenas mejor que texto plano. Este es el terreno.
Todos recurren a AES-256 y se detienen ahí. Pero AES es un cifrador de bloques — el modo de operación es lo que determina si tu cifrado es realmente seguro:
| Modo | Confidencialidad | Integridad / a prueba de manipulación | Veredicto |
|---|---|---|---|
| ECB | Débil — bloques idénticos producen ciphertext idéntico | Ninguna | Nunca usar |
| CBC (sin autenticar) | Sí, con un IV aleatorio | Ninguna — un atacante puede voltear bits sin ser detectado | Evitar |
| GCM (AES-256-GCM) | Sí | Sí — tag de autenticación integrado | Usa este |
AES-256-GCM es un cifrador AEAD — Authenticated Encryption with Associated Data. Te da dos garantías en una sola operación: confidencialidad (los datos son ilegibles sin la clave) e integridad (cualquier manipulación del ciphertext se detecta al descifrar, porque el tag de autenticación no validará). Para almacenar algo tan sensible como una clave de firma, la integridad importa tanto como el secreto — quieres que el descifrado falle en voz alta si una fila fue alterada, no que entregue silenciosamente una clave corrupta a un firmante.
El cementerio de sistemas vulnerados está lleno de cifrados caseros ingeniosos: IVs estáticos, modo ECB, "cifrados" XOR, claves derivadas de valores predecibles, padding a medida. Usa una librería auditada que implemente una construcción AEAD estándar, y sigue las reglas de parámetros de arriba. La criptografía castiga la creatividad.
¿Dónde vive la clave maestra? La respuesta profesional es la envelope encryption: una jerarquía donde una clave de cifrado de claves (KEK) de alto valor — guardada en un Key Management Service (KMS) o un Hardware Security Module (HSM) — cifra las claves de cifrado de datos (DEK) que realmente cifran tus filas. La base de datos solo ve DEKs cifradas; la KEK nunca sale del límite del KMS.
Para un despliegue de un solo tenant, una versión más simple pero sólida es una única clave de 256 bits guardada en un gestor de secretos o una variable de entorno que nunca se escribe en la base de datos, los logs ni el repositorio. El principio innegociable es el mismo a cualquier escala: la clave y el ciphertext deben poder filtrarse de forma independiente. Si un solo compromiso filtra ambos, tienes teatro de cifrado, no cifrado.
Este es el proceso exacto que seguimos — una migración real, en producción, de más de 7.800 claves de wallets vivas en cadenas EVM, TRON, Solana, SUI, Bitcoin, Litecoin y TON, hecha con cero downtime y cero pagos perdidos.
Cada clave privada se cifra con AES-256-GCM. Cada cifrado genera un IV aleatorio fresco de 96 bits y produce un tag de autenticación de 128 bits. Nada se cifra jamás con un IV reutilizado. El valor guardado es un sobre autodescriptivo y versionado:
enc1:<iv base64>:<authTag base64>:<ciphertext base64>El prefijo enc1: versiona el esquema. Permite que cada lectura distinga al instante un valor cifrado de uno en texto plano legacy — que es lo que hace posible una migración gradual y sin roturas.
La clave de 256 bits vive en una variable de entorno provista desde el almacén de secretos del despliegue — nunca en la base de datos, nunca en los logs, nunca en el repositorio git. Una fuga de la base de datos, por tanto, solo entrega ciphertext; la clave se filtra (o no) por un canal completamente separado.
La función de cifrar deja pasar sin cambios un valor ya cifrado; la de descifrar deja pasar sin cambios el texto plano. Esta única propiedad es lo que hace segura la migración: puedes envolver un valor dos veces, o descifrar un valor que nunca se cifró, y nada se rompe. El único error fatal sería olvidar un sitio de lectura — así que hicimos el wrapper imposible de doble-aplicar y auditamos cada ruta.
Un volcado de nuestra base de datos ahora expone exactamente cero claves privadas utilizables. Cada ruta de firma descifra de forma transparente, y la migración se ejecutó sin una sola interrupción a la detección de depósitos, el sweep o los pagos en vivo.
Cifrar las claves es una capa. La seguridad que sobrevive al contacto con la realidad es defensa en profundidad — muchas capas independientes, para que ningún fallo aislado sea fatal. Las prácticas que rodean nuestro cifrado:
El cifrado en reposo es el control que asume que tu base de datos se filtrará y se asegura de que la fuga no valga nada. La receta no es exótica: un cifrador autenticado estándar (AES-256-GCM), un IV único por valor, un tag de autenticación que realmente verificas, y una clave que vive en un lugar distinto de los datos que protege. Envuélvelo en un sobre idempotente y versionado y hasta puedes adaptarlo a un sistema vivo sin downtime — como hicimos nosotros en siete cadenas y miles de wallets.
Si aceptas pagos cripto, hazle a tu pasarela una pregunta: ¿están las claves privadas cifradas en reposo, y dónde vive la clave? Si la respuesta es vaga, las claves probablemente están en texto plano — y a un respaldo filtrado de distancia de un martes muy malo.
Cada wallet que TronDealer genera para ti tiene su clave privada cifrada en reposo con AES-256-GCM. Lee más sobre nuestras garantías en la sección de seguridad de nuestra homepage, o entra a la guía de integración para empezar a aceptar pagos.
En lugar de esparcir llamadas de descifrado por decenas de call sites, desciframos en el puñado de funciones de bajo nivel que convierten un secreto guardado en un firmante — una por blockchain (el constructor de keypair, el firmante de transacciones). Toda ruta que alguna vez firma pasa necesariamente por una de estas, así que un único edit auditable por cadena las cubre todas. Luego verificamos por búsqueda estática que ningún firmante se construye a partir de una clave guardada en crudo fuera de estos puntos.
Como las lecturas aceptan ambos formatos, el código se despliega primero: las wallets nuevas se cifran de inmediato, y las filas existentes en texto plano siguen funcionando (las lecturas las dejan pasar). Luego un script de backfill idempotente barre cada tabla de wallets, cifrando toda fila que no esté ya en forma enc1:. Es re-ejecutable y falla seguro — si una fila da error, se registra y se salta, sin abortar el resto.
Antes de accionar el interruptor, corrimos una verificación de solo lectura contra producción: para una muestra de claves reales en cada cadena, confirmamos que descifrar(cifrar(clave)) reproduce la clave byte por byte, y que la dirección de wallet derivada de la clave que hizo el round-trip es idéntica a la dirección guardada. Si la dirección sale igual, la firma no puede romperse. Salió igual, en cada muestra.