Gravando informações criptografadas #2
No artigo anterior (https://ralms.io/dica/criptografiaefcore/ eu mostrei como criptografar dados no EF Core usando HasConversion.
Criptografia usando EF Core (Annotation)
Usaremos o TripleDESCryptoServiceProvider para criptografar e descriptografar nossas informações, para mais informações sobre TripleDESCryptoServiceProvider, acesse essa URL.
Veja como criptografar dados usando propriedades customizadas com Annotation.
Nossa Classe criptografia
public class Criptografia
{
private static byte[] _chave = Encoding.UTF8.GetBytes("#ef");
public static string Encrypt(string texto)
{
using (var hashProvider = new MD5CryptoServiceProvider())
{
var encriptar = new TripleDESCryptoServiceProvider
{
Mode = CipherMode.ECB,
Key = hashProvider.ComputeHash(_chave),
Padding = PaddingMode.PKCS7
};
using (var transforme = encriptar.CreateEncryptor())
{
var dados = Encoding.UTF8.GetBytes(texto);
return Convert.ToBase64String(transforme.TransformFinalBlock(dados, 0, dados.Length));
}
}
}
public static string Decrypt(string texto)
{
using (var hashProvider = new MD5CryptoServiceProvider())
{
var descriptografar = new TripleDESCryptoServiceProvider
{
Mode = CipherMode.ECB,
Key = hashProvider.ComputeHash(_chave),
Padding = PaddingMode.PKCS7
};
using (var transforme = descriptografar.CreateDecryptor())
{
var dados = Convert.FromBase64String(texto.Replace(" ", "+"));
return Encoding.UTF8.GetString(transforme.TransformFinalBlock(dados, 0, dados.Length));
}
}
}
}
Nosso DBContext
public class ExemploContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Data Source=.\Sistemas;Initial Catalog=TesteCriptografia;Integrated Security=True");
optionsBuilder.UseLoggerFactory(new LoggerFactory().AddConsole());
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Teste>();
foreach (var entidade in modelBuilder.Model.GetEntityTypes())
{
foreach (var propriedade in entidade.GetProperties())
{
var atributos = propriedade
.PropertyInfo
.GetCustomAttributes(typeof(EncripatarAttribute), false);
if (atributos.Any())
{
propriedade.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
}
}
}
Nossa Classe (Entidade)
public class Teste
{
private string informacoes;
public int Id { get; set; }
[Encripatar]
public string Informacoes
{
get => Criptografia.Decrypt(informacoes);
set => informacoes = Criptografia.Encrypt(value);
}
public DateTime Cadastro { get; set; }
}
Nosso Programs.cs
class Program
{
static void Main(string[] args)
{
using (var db = new ExemploContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var dados = new[]
{
new Teste
{
Informacoes = "Informação 01"
},
new Teste
{
Informacoes = "Informação 02"
},
new Teste
{
Informacoes = "Informação 03"
}
};
db.Set<Teste>().AddRange(dados);
db.SaveChanges();
var registros = db
.Set<Teste>()
.AsNoTracking()
.ToList();
// Leitura feita pelo EF Core
Console.WriteLine("Leitura EF Core:");
foreach (var reg in registros)
{
Console.WriteLine($"{reg.Id}-{reg.Informacoes}");
}
// Leitura feita via ADO.NET
Console.WriteLine("\nLeitura ADO.NET:");
using (var cmd = db.Database.GetDbConnection().CreateCommand())
{
db.Database.OpenConnection();
cmd.CommandText = "SELECT [Id],[Informacoes] FROM [Teste]";
using (var ler = cmd.ExecuteReader())
{
while (ler.Read())
{
Console.WriteLine($"{ler.GetInt32(0)}-{ler.GetString(1)}");
}
}
}
}
Console.ReadKey();
}
}
Observe que filtrei todas minhas propriedades customizadas com meu atributo chamado "Encriptar", e informei ao EF Core o tipo de acesso a essa propriedade usando: SetPropertyAccessMode(PropertyAccessMode.Field)
Por exemplo se cometar o seguinte trecho de código:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Teste>();
//foreach (var entidade in modelBuilder.Model.GetEntityTypes())
//{
// foreach (var propriedade in entidade.GetProperties())
// {
// var atributos = propriedade
// .PropertyInfo
// .GetCustomAttributes(typeof(EncripatarAttribute), false);
// if (atributos.Any())
// {
// propriedade.SetPropertyAccessMode(PropertyAccessMode.Field);
// }
// }
//}
}
E tentar ler a propriedade Informacoes:
var lerInformacao = db
.Set<TesteCriptografado>()
.First().Informacoes;
Teremos a seguinte exceção:
A entrada não é uma cadeia de caracteres de Base 64 válida, pois contém um caractere que não é de base 64, mais de dois caracteres de preenchimento ou um caractere ilegal entre os caracteres de preenchimento.
Impacto na performance?
Fiz alguns testes para ter uma base do tempo gasto, em minha máquina inserindo 1000 registros obtive o seguinte resultado:
Total registros | Tempo gravar normal | Tempo gravar criptografado |
---|---|---|
1000 Registros | 00:00:00.1962283 | 00:00:00.3485831 |
1 Registro | 00:00:00.0026146 | 00:00:00.0032140 |
Total registros | Tempo leitura normal | Tempo leitura criptografado |
---|---|---|
1000 Registros | 00:00:00.0239231 | 00:00:00.0241933 |
Se observarmos o impacto de custo sobre (1) um registro é praticamente aceitável, quase imperceptível, o tempo de leitura de (1000 registros) é aceitavel para ambos, porém para quem trabalha fazendo inserts em uma escala de registros maiores, pode ser um pequeno problema, no que se trata do quesito performance, mas aí é onde entra a pergunta: Performance ou segurança?
Para mim os dois tem que trabalhar juntos!!!
Em uma situação que necessite de total segurança das informações, por exemplo: ambiente onde terceiros tenham acesso ao banco de dados, eu prefiro ativar a criptografia, para assegurar que minhas informações estarão mais protegidas.
Url projeto: https://github.com/ralmsdeveloper/ExemplosArtigos
Pessoal, fico por aqui #+1dica!
Deixe um comentário