Actividad #31
cerradaCifrar Datos Sensibles a Nivel de Aplicación
100%
Descripción
Cifrar Datos Sensibles a Nivel de Aplicación
-
Descripción: Implementar cifrado a nivel de aplicación para datos sensibles, como correos electrónicos, números de teléfono de contactos, y claves API, antes de almacenarlos en la base de datos, proporcionando una capa adicional de protección más allá del cifrado de la base de datos.
-
Verificación: Validar que los datos sensibles almacenados son ilegibles sin descifrado y que se pueden recuperar correctamente en la aplicación.
Peticiones relacionadas 2 (0 abiertas — 2 cerradas)
Actualizado por Anibal Pendas Amador hace 4 días
- Copiado de Actividad #30: Cifrar Datos Sensibles a Nivel de Aplicación añadido
Actualizado por Anibal Pendas Amador hace 4 días
- Copiado a Actividad #32: Cifrar Datos Sensibles a Nivel de Aplicación añadido
Actualizado por Airam Cuesta Dueñas hace 1 día
- Estado cambiado de Nuevo a Resuelto
- % Completado cambiado de 0 a 100
Guía: Implementación de Cifrado de Datos Sensibles en ABP Framework¶
Este documento describe los pasos que realicé para implementar cifrado a nivel de aplicación para datos sensibles (correos electrónicos, números de teléfono, claves API) en el Sistema CRM utilizando ABP Framework, antes de almacenarlos en la base de datos. Esto proporciona una capa adicional de protección, complementando el cifrado de la base de datos. Verifiqué que los datos almacenados fueran ilegibles sin descifrado y que se pudieran recuperar correctamente en la aplicación.
Descripción del Objetivo¶
Implementar cifrado a nivel de aplicación para proteger datos sensibles antes de almacenarlos, asegurando:
- Cifrado automático de datos antes del almacenamiento.
- Datos cifrados ilegibles sin descifrado.
- Recuperación correcta de datos en la aplicación.
- Sistema de verificación para confirmar el funcionamiento del cifrado.
Estructura del Proyecto¶
CRM.MVC.Capacitacion/
├── src/
│ ├── CRM.MVC.Capacitacion.Domain/
│ │ └── Services/
│ │ ├── IDataEncryptionService.cs
│ │ └── DataEncryptionService.cs
│ └── CRM.MVC.Capacitacion.HttpApi/
│ └── Controllers/
│ └── EncryptionTestController.cs
Pasos para Implementar el Cifrado¶
Paso 1: Creación de la Interfaz del Servicio de Cifrado¶
-
Archivo:
src/CRM.MVC.Capacitacion.Domain/Services/IDataEncryptionService.cs
-
Contenido:
using System; using Volo.Abp.DependencyInjection; namespace CRM.MVC.Capacitacion.Services { public interface IDataEncryptionService : ITransientDependency { string Encrypt(string plainText); string Decrypt(string encryptedText); bool IsEncrypted(string text); } }
-
Características:
- Hereda de
ITransientDependency
para inyección automática de dependencias. - Define métodos para cifrar, descifrar y verificar el estado de cifrado.
- Interfaz simple y clara.
- Hereda de
Paso 2: Implementación del Servicio de Cifrado¶
-
Archivo:
src/CRM.MVC.Capacitacion.Domain/Services/DataEncryptionService.cs
-
Contenido:
using System; using Microsoft.Extensions.Logging; using Volo.Abp.Security.Encryption; namespace CRM.MVC.Capacitacion.Services { public class DataEncryptionService : IDataEncryptionService { private readonly IStringEncryptionService _stringEncryption; private readonly ILogger<DataEncryptionService> _logger; private const string ENCRYPTED_PREFIX = "ENC:"; public DataEncryptionService( IStringEncryptionService stringEncryption, ILogger<DataEncryptionService> logger) { _stringEncryption = stringEncryption; _logger = logger; } public string Encrypt(string plainText) { if (string.IsNullOrEmpty(plainText)) return plainText; if (IsEncrypted(plainText)) return plainText; try { var encrypted = _stringEncryption.Encrypt(plainText); var result = ENCRYPTED_PREFIX + encrypted; _logger.LogInformation("Data encrypted successfully. Original length: {OriginalLength}, Encrypted length: {EncryptedLength}", plainText.Length, result.Length); return result; } catch (Exception ex) { _logger.LogError(ex, "Error encrypting data"); throw; } } public string Decrypt(string encryptedText) { if (string.IsNullOrEmpty(encryptedText)) return encryptedText; if (!IsEncrypted(encryptedText)) return encryptedText; try { var withoutPrefix = encryptedText.Substring(ENCRYPTED_PREFIX.Length); var decrypted = _stringEncryption.Decrypt(withoutPrefix); _logger.LogInformation("Data decrypted successfully. Encrypted length: {EncryptedLength}, Decrypted length: {DecryptedLength}", encryptedText.Length, decrypted.Length); return decrypted; } catch (Exception ex) { _logger.LogError(ex, "Error decrypting data"); throw; } } public bool IsEncrypted(string text) { return !string.IsNullOrEmpty(text) && text.StartsWith(ENCRYPTED_PREFIX); } } }
-
Características:
- Utiliza
IStringEncryptionService
de ABP para el cifrado. - Añade el prefijo
ENC:
para identificar datos cifrados. - Evita el doble cifrado mediante
IsEncrypted()
. - Incluye registro (logging) para auditoría.
- Maneja errores y valida datos nulos o vacíos.
- Utiliza
Paso 3: Controlador de Prueba para Verificación¶
-
Archivo:
src/CRM.MVC.Capacitacion.HttpApi/Controllers/EncryptionTestController.cs
-
Contenido:
using System; using Microsoft.AspNetCore.Mvc; using CRM.MVC.Capacitacion.Services; using Volo.Abp.AspNetCore.Mvc; namespace CRM.MVC.Capacitacion.Controllers { [Route("api/encryption-test")] [ApiController] public class EncryptionTestController : AbpController { private readonly IDataEncryptionService _encryptionService; public EncryptionTestController(IDataEncryptionService encryptionService) { _encryptionService = encryptionService; } [HttpPost("test")] public IActionResult TestEncryption([FromBody] TestDataRequest request) { try { // Cifrar datos sensibles var encryptedEmail = _encryptionService.Encrypt(request.Email); var encryptedPhone = _encryptionService.Encrypt(request.Phone); var encryptedApiKey = _encryptionService.Encrypt(request.ApiKey); // Descifrar para verificar var decryptedEmail = _encryptionService.Decrypt(encryptedEmail); var decryptedPhone = _encryptionService.Decrypt(encryptedPhone); var decryptedApiKey = _encryptionService.Decrypt(encryptedApiKey); return Ok(new { Original = new { Email = request.Email, Phone = request.Phone, ApiKey = request.ApiKey }, Encrypted = new { Email = encryptedEmail, Phone = encryptedPhone, ApiKey = encryptedApiKey, EmailIsEncrypted = _encryptionService.IsEncrypted(encryptedEmail), PhoneIsEncrypted = _encryptionService.IsEncrypted(encryptedPhone), ApiKeyIsEncrypted = _encryptionService.IsEncrypted(encryptedApiKey) }, Decrypted = new { Email = decryptedEmail, Phone = decryptedPhone, ApiKey = decryptedApiKey }, Verification = new { EmailMatches = request.Email == decryptedEmail, PhoneMatches = request.Phone == decryptedPhone, ApiKeyMatches = request.ApiKey == decryptedApiKey, AllDataIntact = request.Email == decryptedEmail && request.Phone == decryptedPhone && request.ApiKey == decryptedApiKey } }); } catch (Exception ex) { return BadRequest(new { Error = ex.Message }); } } } public class TestDataRequest { public string Email { get; set; } public string Phone { get; set; } public string ApiKey { get; set; } } }
-
Funcionalidades:
- Endpoint
POST /api/encryption-test/test
para probar el cifrado. - Acepta datos sensibles de prueba (email, teléfono, clave API).
- Devuelve datos originales, cifrados, descifrados y verificaciones.
- Valida la integridad de los datos.
- Endpoint
Paso 4: Configuración de Referencias del Proyecto¶
- Modifiqué el archivo
src/CRM.MVC.Capacitacion.HttpApi/CRM.MVC.Capacitacion.HttpApi.csproj
para incluir la referencia al proyectoDomain
:<ItemGroup> <ProjectReference Include="..\CRM.MVC.Capacitacion.Application.Contracts\CRM.MVC.Capacitacion.Application.Contracts.csproj" /> <ProjectReference Include="..\CRM.MVC.Capacitacion.Domain\CRM.MVC.Capacitacion.Domain.csproj" /> </ItemGroup>
Paso 5: Compilación y Preparación del Entorno¶
-
Compilé el proyecto:
dotnet build
-
Generé un certificado OpenIddict (si es necesario):
dotnet dev-certs https -v -ep openiddict.pfx -p 9f6c661e-4a1d-4873-9e03-c8646c941e28
-
Copié el certificado al proyecto web:
copy openiddict.pfx src\CRM.MVC.Capacitacion.Web\openiddict.pfx
Paso 6: Ejecución y Pruebas¶
-
Ejecuté la aplicación:
dotnet run --project src/CRM.MVC.Capacitacion.Web --urls "https://localhost:44362"
-
Probé el endpoint de cifrado:
-
Opción A: Usar Swagger UI:
- Abrí el navegador en
https://localhost:44362/swagger
. - Localicé el endpoint
EncryptionTest
. - Ejecuté con datos de prueba:
{ "email": "usuario@ejemplo.com", "phone": "+34123456789", "apiKey": "mi-api-key-secreta-123" }
- Abrí el navegador en
-
Opción B: Usar cURL:
curl -X POST "https://localhost:44362/api/encryption-test/test" \ -H "Content-Type: application/json" \ -d "{\"email\":\"usuario@ejemplo.com\",\"phone\":\"+34123456789\",\"apiKey\":\"mi-api-key-secreta-123\"}" \ -k
-
Opción A: Usar Swagger UI:
Paso 7: Verificación de Resultados¶
-
Respuesta esperada del API:
{ "original": { "email": "usuario@ejemplo.com", "phone": "+34123456789", "apiKey": "mi-api-key-secreta-123" }, "encrypted": { "email": "ENC:uv22fKdTr5E4ThGfen@WuifPutl2H2zfb4N@cZDJdHw==", "phone": "ENC:t2V@kLDYn3Nu/nJ7YN8Zg==", "apiKey": "ENC:@PMgmysw5qEtxiOvaKUtSsJjGwTa5pbnVImTTMy@c=", "emailIsEncrypted": true, "phoneIsEncrypted": true, "apiKeyIsEncrypted": true }, "decrypted": { "email": "usuario@ejemplo.com", "phone": "+34123456789", "apiKey": "mi-api-key-secreta-123" }, "verification": { "emailMatches": true, "phoneMatches": true, "apiKeyMatches": true, "allDataIntact": true } }
-
Criterios de éxito:
- Datos originales: Legibles.
- Datos cifrados: Con prefijo
ENC:
y texto ilegible. - Datos descifrados: Idénticos a los originales.
- Verificación: Todos los campos en
true
.
Paso 8: Implementación en Entidades Reales¶
-
Ejemplo de uso en una entidad (
ContactoEntity
):public class ContactoEntity : FullAuditedAggregateRoot<Guid> { private readonly IDataEncryptionService _encryptionService; public string Nombre { get; set; } private string _email; private string _telefono; public string Email { get => _encryptionService?.Decrypt(_email) ?? _email; set => _email = _encryptionService?.Encrypt(value) ?? value; } public string Telefono { get => _encryptionService?.Decrypt(_telefono) ?? _telefono; set => _telefono = _encryptionService?.Encrypt(value) ?? value; } }
Características de Seguridad Implementadas¶
-
Funcionalidades principales:
- Cifrado automático antes del almacenamiento.
- Descifrado transparente al recuperar datos.
- Identificación de datos cifrados con prefijo
ENC:
. - Prevención de doble cifrado mediante
IsEncrypted()
. - Registro (logging) para auditoría de operaciones.
- Manejo robusto de excepciones.
-
Seguridad:
- Capa adicional de protección más allá del cifrado de la base de datos.
- Datos ilegibles sin descifrado.
- Integración con el módulo de seguridad nativo de ABP.
- Servicio disponible en toda la aplicación mediante inyección de dependencias.
Casos de Uso Recomendados¶
-
Datos sensibles a cifrar:
- Correos electrónicos.
- Números de teléfono.
- Claves API.
- Tokens de acceso.
- Información personal identificable (PII).
- Datos bancarios o financieros.
- Direcciones físicas.
-
Consideraciones de rendimiento:
- Impacto mínimo en el rendimiento para datos pequeños.
- Los datos cifrados no son buscables directamente.
- Los datos cifrados ocupan más espacio de almacenamiento.
Resolución de Problemas¶
-
Error de compilación (namespace no encontrado):
-
Solución: Verificar que se agregó la referencia al proyecto
Domain
en el archivo.csproj
.
-
Solución: Verificar que se agregó la referencia al proyecto
-
Certificado OpenIddict no encontrado:
- Solución: Generar y copiar el certificado como se indica en el Paso 5.
-
MySQL no conecta:
- Solución: Verificar que el servicio MySQL esté ejecutándose.
-
El endpoint no aparece en Swagger:
- Solución: Asegurar que el controlador esté en el namespace correcto.
Conclusión¶
Implementé una solución robusta y segura para el cifrado de datos sensibles a nivel de aplicación en el Sistema CRM utilizando ABP Framework. El sistema es:
- Fácil de implementar.
- Transparente para el desarrollador.
- Altamente seguro.
- Completamente verificable.
- Integrado con ABP.
La verificación mediante el endpoint de prueba confirmó que los datos sensibles se cifran correctamente, son ilegibles sin descifrado y se recuperan correctamente en la aplicación.
Actualizado por Anibal Pendas Amador hace aproximadamente 23 horas
- Estado cambiado de Resuelto a Cerrado
Buen trabajo