Cifrado de extremo a extremo (E2EE)
El E2EE garantiza que el contenido sensible de una conversación no se almacene en texto plano en el servidor. En este proyecto el cifrado en reposo usa AES con clave de 256 bits por conversación.
Algoritmo y almacenamiento de claves
- Implementación: clase estática
E2EEncryptionServiceenInfrastructure/Security/. - Al crear una conversación directa,
GetOrCreateConversationHandlergenera una clave conE2EEncryptionService.GenerateKey()y la guarda enConversation.EncryptionKey(Base64 en MongoDB). - Solo los mensajes de tipo TEXT con contenido no vacío se cifran antes de persistir.
Flujo de cifrado
Cliente (emisor) Backend Cliente (receptor)
│ │ │
│ texto plano (HTTPS) │ │
│─────────────────────────►│ │
│ GetEncryptionKeyAsync │
│ E2EEncryptionService.Encrypt │
│ guardar content cifrado en MongoDB │
│ SignalR ReceiveMessage (payload según API) │
│ │──────────────────────────────────►│
│ │ descifrado
│ │ en cliente*
* La API puede devolver contenido descifrado en lecturas HTTP (GetMessagesHandler usa Decrypt al armar la respuesta). Ajusta el contrato del cliente según tu modelo de confianza.
Etapas
- El usuario envía texto por HTTP o SignalR (
SendMessage). SendMessageHandlerobtiene la clave de la conversación y llama aEncrypt(plainText, keyBytes).- MongoDB persiste
Message.Contentcifrado (Base64: IV + ciphertext). - Eventos en tiempo real y lecturas posteriores usan la misma clave de conversación.
Implementación real (resumen)
public static class E2EEncryptionService
{
public static byte[] GenerateKey() { /* Aes.Create().GenerateKey() */ }
public static string Encrypt(string plainText, byte[] key)
{
// IV de 16 bytes prefijado al ciphertext, todo en Base64
}
public static string Decrypt(string cipherText, byte[] key)
{
// Extrae IV, descifra con AES-CBC vía CryptoStream
}
}
Archivo completo en el repositorio: uni-chat-backend/Infrastructure/Security/E2EEncryptionService.cs.
Garantías y límites
| Escenario | Comportamiento |
|---|---|
| Tránsito | HTTPS + JWT en API y SignalR |
| Repositorio | content cifrado para texto |
| Por conversación | Clave distinta (EncryptionKey en Conversation) |
| Archivos multimedia | Se almacenan URL en Cloudinary; no pasan por el mismo cifrado de texto |
Importante: No persistas ni registres en logs el texto plano de mensajes. El pre-commit del repo ayuda a evitar commitear
.envy secretos.
Otras medidas de seguridad
| Mecanismo | Función |
|---|---|
| JWT + Refresh Token | Autenticación y renovación de sesión |
[Authorize] en hub y endpoints |
Acceso solo con token válido |
| Rate limiting | Mitigación de abuso en la API |
| Secretos fuera del repo | GitHub Secrets + render-env.sh — ver CI/CD y secrets |