Actividad #30 » GUIA_CIFRADO_DATOS_SENSIBLES.md
Guía: Implementación de Cifrado de Datos Sensibles en ABP Framework
📋 Descripción del Objetivo
Implementar cifrado a nivel de aplicación para datos sensibles (correos electrónicos, números de teléfono, claves API) antes de almacenarlos en la base de datos, proporcionando una capa adicional de protección.
🎯 Resultado Esperado
- Los datos sensibles se cifran automáticamente antes del almacenamiento
- Los datos cifrados son ilegibles sin descifrado
- Los datos se pueden recuperar correctamente en la aplicación
- Sistema de verificación para comprobar que el cifrado funciona
📁 Estructura del Proyecto
CRM.MVC.Capacitacion/
├── src/
│ ├── CRM.MVC.Capacitacion.Domain/
│ │ └── Services/
│ │ ├── IDataEncryptionService.cs
│ │ └── DataEncryptionService.cs
│ └── CRM.MVC.Capacitacion.HttpApi/
│ └── Controllers/
│ └── EncryptionTestController.cs
🛠️ Paso 1: Creación de la Interface del Servicio de Cifrado
Archivo: src/CRM.MVC.Capacitacion.Domain/Services/IDataEncryptionService.cs
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 - ✅ Métodos para cifrar, descifrar y verificar estado de cifrado
- ✅ Interface simple y limpia
🔐 Paso 2: Implementación del Servicio de Cifrado
Archivo: src/CRM.MVC.Capacitacion.Domain/Services/DataEncryptionService.cs
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 Clave:
- ✅ Utiliza
Volo.Abp.Security.Encryption.IStringEncryptionService
- ✅ Prefijo "ENC:" para identificar datos cifrados
- ✅ Previene doble cifrado con
IsEncrypted()
- ✅ Logging detallado para auditoría
- ✅ Manejo robusto de errores
- ✅ Validación de datos nulos/vacíos
🌐 Paso 3: Controlador de Prueba para Verificación
Archivo: src/CRM.MVC.Capacitacion.HttpApi/Controllers/EncryptionTestController.cs
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 del Controlador:
- ✅ Endpoint para probar el cifrado:
POST /api/encryption-test/test
- ✅ Acepta datos sensibles de prueba
- ✅ Demuestra el proceso completo: cifrado → descifrado → verificación
- ✅ Respuesta detallada con todos los estados de los datos
- ✅ Validación de integridad de datos
🔗 Paso 4: Configuración de Referencias del Proyecto
Modificar src/CRM.MVC.Capacitacion.HttpApi/CRM.MVC.Capacitacion.HttpApi.csproj
Agregar la referencia al proyecto Domain:
<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
5.1 Compilar el Proyecto
dotnet build
5.2 Generar Certificado OpenIddict (si es necesario)
dotnet dev-certs https -v -ep openiddict.pfx -p 9f6c661e-4a1d-4873-9e03-c8646c941e28
5.3 Copiar Certificado al Proyecto Web
copy openiddict.pfx src\CRM.MVC.Capacitacion.Web\openiddict.pfx
🚀 Paso 6: Ejecución y Pruebas
6.1 Ejecutar la Aplicación
dotnet run --project src/CRM.MVC.Capacitacion.Web --urls "https://localhost:44362"
6.2 Probar el Endpoint de Cifrado
Opción A: Usar Swagger UI
- Abrir navegador en:
https://localhost:44362/swagger
- Localizar el endpoint
EncryptionTest
- Ejecutar con datos de prueba:
{
"email": "usuario@ejemplo.com",
"phone": "+34123456789",
"apiKey": "mi-api-key-secreta-123"
}
Opción B: Usar cURL
curl -X POST "https://localhost:44362/api/encryption-test/test" \
-H "Content-Type: application/json" \
-d "{\"email\":\"test@example.com\",\"phone\":\"+1234567890\",\"apiKey\":\"secret-api-key-123\"}" \
-k
✅ 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: Claramente legibles
- Datos cifrados: Prefijo "ENC:" + 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:
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: Los datos se cifran antes del almacenamiento
- Descifrado Transparente: Los datos se descifran al recuperarse
- Identificación de Estado: Prefijo "ENC:" para datos cifrados
- Prevención de Doble Cifrado: Detecta datos ya cifrados
- Logging de Auditoría: Registra operaciones de cifrado/descifrado
- Manejo de Errores: Gestión robusta de excepciones
🛡️ Seguridad:
- Capa Adicional de Protección: Más allá del cifrado de base de datos
- Datos Ilegibles: Sin descifrado, los datos son completamente ilegibles
- Integración ABP: Utiliza el módulo de seguridad nativo de ABP
- Inyección de Dependencias: Servicio disponible en toda la aplicación
🎯 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/financieros
- ✅ Direcciones físicas
Consideraciones de Rendimiento:
- ⚡ Impacto mínimo: El cifrado es rápido para datos pequeños
- 🔍 Búsquedas: Los datos cifrados no son buscables directamente
- 💾 Almacenamiento: Los datos cifrados ocupan más espacio
🔧 Troubleshooting
Problemas Comunes:
-
Error de Compilación - Namespace no encontrado:
- Solución: Verificar que se agregó la referencia al proyecto Domain
-
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: Verificar que el controlador esté en el namespace correcto
📝 Conclusión
Esta implementación proporciona una solución robusta y segura para el cifrado de datos sensibles a nivel de aplicación en proyectos 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 garantiza que el sistema funciona correctamente antes de implementarlo en entidades de producción.
- « Anterior
- 1
- 2
- Siguiente »