2010-07-21 44 views
8

¿Alguien ha descubierto una buena forma de extraer valores encriptados de db a través de entity framework 4?Columnas cifradas con Entity Framework

Obtuve un MySql db con algunas columnas cifradas con des_encrypt y necesito poder conseguir esos valores lo más fácil posible, y también, por supuesto, actualizarlos e insertarlos.

Creo que es bastante extraño que no parezca haber soporte integrado para esto en EF. Incluso nuestro propio sistema ORM incorporado tiene soporte para esto. Solo agregamos un comentario "encriptado" para cada campo que está encriptado y la herramienta ORM agregará des_decrypt (columna) y des_encrypt (columna) en las consultas.

¿Alguien?

Respuesta

2

IMO debe cifrar antes de ponerlo en la base de datos y almacenarlo como datos binarios. Entonces puede obtener fácilmente el byte[] con EF.

EDITAR: ¿Qué pasa si utilizó un procedimiento almacenado para hacer todos los des_encrypt y des_decrypt, así como el selects/inserts/deletes para usted. Entonces EF todavía hará el mapeo por ti?

+0

Parece una buena solución y probablemente lo haría si crease una nueva base de datos. La cuestión es que la base de datos es bastante grande y es utilizada por toneladas de proyectos, por lo que sería un gran trabajo revisar el código y cambiar esto. – Andreas

+0

@Andreas - Ver mi edición anterior. – TheCloudlessSky

+0

Gracias por la sugerencia. Esto parecía una muy buena idea y lo probé. lamentablemente, si quiero poder hacer consultas LINQ en todos mis datos descifrados de la tabla, el procedimiento almacenado debe ejecutarse primero. Esto lleva mucho tiempo, ya que son más de 250 000 filas con 5 columnas para descifrar. Haciendo esto: context.AllMembers(). Where (x => x.MemberId == 1) llevará demasiado tiempo. Claro que podría hacer un SP que tome un argumento de memberid, pero ¿qué sucede si quiero buscar en, por ejemplo primer nombre con LINQ? Quizás estoy perdiendo algo importante aquí ... – Andreas

1

Puede utilizar el cifrado AES (cifrado bidireccional). Cuando necesite consultar el archivo db, puede enviar la cadena cifrada que puede representar el valor objetivo.

Puede crear una extensión para descifrar la entidad.

MyTableEntitiesSet.Where(c=>c.MyField == MySeekValue.Encrypt()).First().Decrypt(); 

Esto puede hacer una consulta en la base de datos.

Tenga en cuenta el tamaño de los datos, los datos cifrados es más grande ...

+4

Pregunta y respuesta antiguas, pero ten en cuenta que si tratas de hacerlo, solo funcionará si utilizas un Vector Init fijo, que no se recomienda, ya que lo haría potencialmente permitir que un atacante conozca los datos. Debería usarse una IV aleatoria con cada encriptación, lo que significaría que obtendría un valor diferente cada vez que cifre algo. –

-3

En mi caso específico que necesitaba para cifrar un número de tarjeta de crédito, que es siempre 16 caracteres; así que acabo de agregar una condición en el get (¡si es la longitud! = 16 luego descifrar) y en el conjunto (si es la longitud == 16 luego encriptar) de la propiedad. Funciona y me evitó mucho trabajo.

12

Este es un ejemplo de implementación de la respuesta propuesta por @TheCloudlessSky. Pensé que ayudaría a cualquiera que se preguntara cómo implementarlo.

Estaba trabajando con una base de datos existente, por lo que la clase de modelo básico se generó automáticamente para mí.

User.cs generada automáticamente:

namespace MyApp.Model 
{ 
    public partial class User 
    { 
     public int UserId { get; set; } 
     public byte[] SSN { get; set; } 
     ... 
    } 
} 

he creado mi propia User.cs. (Tenga en cuenta que está en el mismo espacio de nombres que los User.cs generados automáticamente y no hubo errores en el compilador porque el User.cG generado automáticamente se declaró como clase parcial! Además, mis propios User.cs no pueden estar en la misma carpeta que User.cs generados automáticamente a causa de conflicto de nombre de archivo!)

namespace MyApp.Model 
{ 
    public partial class User 
    { 
     public string DecryptedSSN { get; set; } 
     ... 
    } 
} 

Ahora cada vez que tuviera que recuperar usuario de mi DbContext, voy a ver todas las propiedades definidas en la clase de auto-generado, así como los definidos en mi clase mejorada

Aquí hay una implementación de mi UserRepository.CS:

namespace MyApp.Model 
{ 
    public interface IUserRepository 
    { 
     User Get(int userId); 
     ... 
    } 

    public class UserRepository : IUserRepository 
    { 
     public User GetById(int userId) 
     { 
      using (var dataContext = MyDbContext()) 
      { 
       var user = dataContext.Users.Find(u => u.UserId == userId); 
       var decryptedSSNResult = dataContext.Decrypt(u.SSN); 
       user.DecryptedSSN = decryptedSSNResult.FirstOrDefault(); 
       return user; 
      } 
     } 
    } 
} 

Ahora usted puede preguntarse cómo/dónde he llegado MyDbContext.Decrypt() desde?

Esto NO se genera automáticamente para usted. Sin embargo, puede importar este procedimiento almacenado en su archivo Model.Context.cs generado automáticamente. (Este proceso está muy bien documentado en el artículo oficial de EntityFramework: Cómo: Importar un procedimiento almacenado (Entity Data Model Tools) http://msdn.microsoft.com/en-us/library/vstudio/bb896231(v=vs.100).aspx)

Por si acaso no sabe cómo debería ser el resultado final, aquí está lo que se genera automáticamente en mis Model.Context.cs:

namespace MyApp.Model 
{ 
    // using statements found here 

    public partial class MyDbContext : DbContext 
    { 
     public MyDbContext() 
      : base("name = MyDbContext") 
     { } 

     public virtual ObjectResult<string> Decrypt(byte[] encryptedData) 
     { 
      var encryptedDataParameter = encryptedData != null ? 
          new ObjectParameter("encryptedData", encryptedData) : 
          new ObjectParameter("encryptedData", typeof(byte[])); 

      return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<string>("Decrypt", encryptedDataParameter); 
     } 

     // similar function for Encrypt 
    } 
} 

Así es como mi Descifrar procedimiento almacenado se parece a:

CREATE PROCEDURE decrypt 
    @encryptedData VARBINARY(8000) 
AS 
BEGIN 
    OPEN SYMMETRIC KEY xxx_Key DECRYPTION BY CERTIFICATE xxx_Cert; 

    SELECT CAST(DECRYPTIONBYKEY(@encryptedData) AS NVARCHAR(MAX)) AS data; 

    CLOSE ALL SYMMETRIC KEYS; 
END; 
GO 

Consideraciones de rendimiento

Ahora que le he mostrado una implementación de respuesta dada por @TheCloudlessSky, me gustaría resaltar rápidamente algunos puntos relacionados con el rendimiento.

1) Cada vez que recupera un objeto de usuario, se realizan 2 viajes a la base de datos en lugar de 1. Primer viaje para recuperar el objeto; segundo viaje para descifrar el SSN. Esto puede causar problemas de rendimiento si no tiene cuidado.

Recomendación: ¡NO descifre automáticamente los campos encriptados! En mi ejemplo que se muestra arriba, descifré el SSN cuando estaba recuperando un objeto de usuario. ¡Lo hice solo con fines de demostración! Pregúntese si realmente necesita SSN cada vez que se recupera el usuario. Si es posible, ¡elija descifrado perezoso sobre desencriptación ansiosa!

2) Si bien no lo he demostrado, cada vez que crea/actualiza un objeto de usuario, también se realizarán 2 viajes a la base de datos. Primer viaje para cifrar SSN; segundo viaje para insertar el objeto. De nuevo, esto puede causar problemas de rendimiento si no tiene cuidado.

Recomendación: Sea consciente de este rendimiento pero no delegue el cifrado y el guardado de SSN como un método diferente. Manténgalo todo en una sola operación, de lo contrario, puede olvidarse de guardarlo por completo. Por lo tanto, la recomendación para crear/actualizar es lo opuesto a la recuperación: ¡elija un encriptado ansioso sobre un cifrado lento!

+0

Te doy un voto por el esfuerzo. Pero no estoy de acuerdo con su enfoque un poco. No quiero que mi base de datos esté descifrando valores. Puedo almacenar toda esa información en mi aplicación y no tener que hacer dos viajes a la base de datos. – mac10688

+0

@ mac10688 Estoy de acuerdo con usted en el golpe de rendimiento asociado con múltiples viajes a la base de datos.En general, escribir procedimientos almacenados dedicados para operaciones CUD y vincularlos al modelo a través del marco de la entidad es más eficiente que tener procedimientos almacenados como cifrar/descifrar. Pero quería demostrar un método para lograr el cifrado/descifrado utilizando claves administradas por la base de datos en lugar de la aplicación. –

+0

Proporciono un enlace sobre cómo conectar los procedimientos almacenados de CUD a las operaciones de CUD por parte del marco de trabajo de la entidad en caso de que alguien tenga curiosidad sobre cómo lograr esto: https://msdn.microsoft.com/en-us/data/jj593489. Sé que los enlaces en general son mal vistos, pero dado que este es para el sitio de documentación de MSDN Entity Framework, espero que a las personas no les importe. –

1

Puede ir a la seguridad de encriptación de bricolaje/roll-your pero cada experto en seguridad le dirá never, ever, do that. La parte más difícil de la seguridad y el cifrado de datos no es realmente "AES" o algún algoritmo. Es la gestión de claves. Tarde o temprano, se enfrentará a esta bestia y es manera más difícil.

Afortunadamente, hay una herramienta llamada Crypteron CipherDb que se ocupa de eso. De hecho, va más allá del cifrado de marco de entidad, también proporciona protección contra manipulación automática, almacenamiento de clave segura, distribución de clave segura, transferencias de claves, almacenamiento en memoria caché de claves, listas de control de acceso y más. Existe una edición gratuita para la comunidad y solo le tomará unos minutos agregarla a su aplicación.

Cuando se integra con el marco de la entidad, que acaba de realizar anotaciones en el modelo de datos con [Secure] o el nombre de una propiedad a algo así como Secure_SocialSecurityNumber (el Secure_ es la parte clave) y CipherDb se encarga del resto.

Por ejemplo, el modelo de datos sería:

public class Patient 
{ 
    public int Id {get; set;} 

    [Secure] 
    public string FullName {get; set;} 

    [Secure] 
    public string SocialSecurityNumber {get; set;} 
} 

Y tu web.config sería

<configuration> 
    <configSections> 
    <section 
     name="crypteronConfig" 
     type="Crypteron.CrypteronConfig, CipherCore, Version=2017, Culture=neutral, PublicKeyToken=e0287981ec67bb47" 
     requirePermission="false" /> 
    </configSections> 

    <crypteronConfig> 
    <myCrypteronAccount appSecret="Get_this_from_http://my.crypteron.com" /> 
    </crypteronConfig> 
</configuration> 

Se recomienda asegurar su web.config O conecte la llave API Crypteron (AppSecret) programáticamente (documentation)

Puede encontrar las aplicaciones de muestra en GitHub al https://github.com/crypteron/crypteron-sample-apps. . Por cierto, la edición gratuita se beneficia de las ofertas comerciales, por lo que además de lo anterior, también puede asegurar secuencias, archivos, objetos, colas de mensajes, bases de datos NoSQL, etc., todo desde un solo lugar.

Descargo de responsabilidad: Trabajo allí y tenemos una edición de comunidad gratuita que cualquiera puede usar (y no hacemos ningún dinero). Si crees que es genial, cuéntanoslo, cuéntaselo a tus amigos. Si tiene un presupuesto, obtenga una licencia comercial. Nos ayuda a ofrecer la edición gratuita para todo el mundo :)