2010-07-21 9 views
8

Qualcuno ha trovato un modo valido per estrarre i valori crittografati da db attraverso l'entità framework 4?Colonne crittografate con Entity Framework

Ho un MySQL db con alcune colonne crittografate con des_encrypt e ho bisogno di essere in grado di ottenere quei valori il più semplice possibile e, naturalmente, di aggiornarli e inserirli.

Penso che sia abbastanza strano che non ci sia supporto per questo in EF. Anche il nostro sistema ORM costruito ha supporto per questo. Basta aggiungere un commento "crittografato" per ogni campo che è crittografato e lo strumento ORM aggiungerà des_decrypt (colonna) e des_encrypt (colonna) nelle query.

Chiunque?

risposta

2

IMO è necessario crittografare prima del inserendolo nel database e memorizzarlo come dati binari. Quindi puoi facilmente ottenere byte[] con EF.

MODIFICA: Che cosa succede se si è utilizzata una stored procedure per eseguire tutti i numeri des_encrypt e des_decrypt nonché per il numero selects/inserts/deletes. Allora EF continuerà a fare la mappatura per te?

+0

sembra una buona soluzione e mi sarebbe probabilmente andare con che, se ho creato un nuovo database. Il fatto è che il database è piuttosto grande e viene utilizzato da tonnellate di progetti, quindi sarebbe un lavoro enorme scavalcare il codice e cambiarlo. – Andreas

+0

@Andreas - Vedere la mia modifica sopra. – TheCloudlessSky

+0

Grazie per il suggerimento. Questa mi è sembrata una buona idea e l'ho provata. purtroppo se voglio essere in grado di fare query LINQ in tutti i miei dati decifrati dalla tabella, la stored procedure deve prima eseguire. Ciò richiede un tempo infinito poiché sono 250.000+ righe con 5 colonne ciascuna da decifrare. Così farlo: context.AllMembers() Dove (x => x.MemberId == 1) avrà troppo tempo.. Certo che potrei fare un SP che prende un argomento di memberid, ma cosa succede se voglio cercare su ad es. nome con LINQ? Forse im manca qualcosa di importante qui ... – Andreas

1

È possibile utilizzare AES Encryption (crittografia a 2 vie). Quando è necessario interrogare il db, è possibile inviare la stringa crittografata che può rappresentare il valore di destinazione.

È possibile creare un'estensione per decrittografare l'entità.

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

Questo può eseguire una query di database.

Essere consapevoli delle dimensioni dei dati, dati crittografati è più grande ...

+4

Vecchio domanda e risposta, ma basta essere consapevoli che se si tenta di fare questo, funziona solo se si utilizza un Init vettore fisso, che non è raccomandato, poiché imporrebbe potenzialmente consentire a un utente malintenzionato di conoscere i dati. Dovrebbe essere usata una IV casuale con ogni crittografia, il che significherebbe che si otterrebbe un valore diverso ogni volta che si cripta qualcosa. –

-3

Nel mio caso specifico ho bisogno di cifrare un numero di carta di credito, che è sempre di 16 caratteri; quindi ho appena aggiunto una condizione nel get (se lunghezza! = 16 quindi decrittografata) e nel set (se lunghezza == 16 poi criptato) della proprietà. Funziona e mi evita molto lavoro.

12

Questo è un esempio di implementazione della risposta proposta da @TheCloudlessSky. Ho pensato che avrebbe aiutato qualcuno che si stava chiedendo come implementarlo.

Stavo lavorando con un database esistente, quindi la classe di base del modello è stata generata automaticamente per me.

User.cs generato automaticamente:

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

ho creato il mio User.cs. (Si noti che si trova nello stesso spazio dei nomi del file User.cs generato automaticamente e non ci sono stati errori del compilatore perché il file User.cs generato automaticamente è stato dichiarato come classe parziale! Inoltre, il mio User.cs non può essere nella stessa cartella di User.cs auto-generate a causa del nome del file di conflitto!)

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

Ora ogni volta che dovessi recuperare utente dal mio DbContext, vedrò tutte le proprietà definite nella classe generata automaticamente, così come quelle definite nelle la mia classe migliorata.

Ecco una implementazione del mio 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; 
      } 
     } 
    } 
} 

Ora si potrebbe chiedere come/dove ho ricevuto MyDbContext.Decrypt() da?

Questo NON viene generato automaticamente. Tuttavia, è possibile importare questa procedura memorizzata nel file Model.Context.cs generato automaticamente. (Questo processo è molto ben documentato nell'articolo ufficiale di EntityFramework: Come: Importare una stored procedure (Strumenti modello Entity Data) al http://msdn.microsoft.com/en-us/library/vstudio/bb896231(v=vs.100).aspx)

Nel caso in cui non si sa quale dovrebbe essere il risultato finale, qui è ciò che è stato generato automaticamente nei miei 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 
    } 
} 

Ecco come il mio Decrypt stored procedure assomiglia:

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 

Considerazioni sulle prestazioni

Ora che vi ho mostrato un'implementazione della risposta data dal @TheCloudlessSky, io desidero sottolineare rapidamente alcuni punti relativi alle prestazioni.

1) Ogni volta che si recupera un oggetto utente, al database vengono effettuati 2 viaggi anziché 1. Primo viaggio per il recupero dell'oggetto; secondo viaggio per decifrare SSN. Questo può causare problemi di prestazioni se non si presta attenzione.

Raccomandazione: NON decodificare automaticamente i campi crittografati! Nel mio esempio mostrato sopra, ho decodificato SSN durante il recupero dell'oggetto utente. L'ho fatto solo a scopo dimostrativo! Chiediti se hai davvero bisogno di SSN ogni volta che l'utente viene richiamato. Se possibile, scegli la decodifica pigro su una decifratura entusiasta!

2) Anche se non l'ho dimostrato, ogni volta che si crea/aggiorna un oggetto utente, ci saranno anche 2 viaggi nel database. Primo viaggio per la crittografia SSN; secondo viaggio per l'inserimento dell'oggetto. Di nuovo questo può causare problemi di prestazioni se non stai attento.

Raccomandazione: essere consapevoli di questo problema di prestazioni ma non delegare la crittografia e il salvataggio di SSN come metodo diverso. Mantieni tutto in un'unica operazione altrimenti potresti dimenticarti di salvarlo del tutto. Quindi la raccomandazione per la creazione/l'aggiornamento è opposta al recupero: scegli una codifica desiderosa su una crittografia pigra!

+0

Ti do un voto per lo sforzo. Ma non sono d'accordo con il tuo approccio un po '. Non voglio che il mio database decifri i valori. Posso memorizzare tutte queste informazioni nella mia applicazione e non dover effettuare due viaggi nel database. – mac10688

+0

@ mac10688 Sono d'accordo con voi sul risultato di prestazioni associato a più viaggi nel database.In generale, scrivere stored procedure dedicate per le operazioni CUD e collegarle al modello tramite il framework di entità è più efficiente rispetto all'aver memorizzato le procedure come criptare/decodificare. Ma volevo dimostrare un metodo per ottenere la crittografia/decrittografia usando le chiavi gestite dal database invece dell'applicazione. –

+0

Fornisco un collegamento a come collegare le procedure memorizzate CUD alle operazioni CUD dal framework entità nel caso in cui qualcuno sia curioso su come ottenere ciò: https://msdn.microsoft.com/en-us/data/jj593489. So che i link sono generalmente disapprovati, ma poiché questo è per il sito MSDN Entity Framework Documentation, spero che alla gente non dispiacerà. –

1

Si può fare il fai-da-te/roll-la propria sicurezza di crittografia ma ogni esperto di sicurezza ti dirà di never, ever, do that. La parte più difficile della sicurezza e della crittografia dei dati non è in realtà "AES" o qualche algoritmo. È la gestione delle chiavi. Prima o poi, affronterai questa bestia ed è il modo più difficile.

Fortunatamente, c'è uno strumento chiamato Crypteron CipherDb che si occupa di questo. In realtà va oltre la crittografia Entity Framework, che fornisce anche protezione automatica di manomissione, stoccaggio chiave sicura, distribuzione di chiavi sicure, chiave roll-over, caching chiave, elenchi di controllo di accesso e altro ancora. C'è un'edizione comunità libera e richiede solo pochi minuti per aggiungere alla vostra applicazione.

Quando l'integrazione con Entity Framework, basta annotare il modello di dati con [Secure] o il nome di una proprietà a qualcosa come Secure_SocialSecurityNumber (il Secure_ è la parte fondamentale) e CipherDb si prende cura di tutto il resto.

Ad esempio, il modello di dati sarebbe:

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

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

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

E web.config sarebbe

<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> 

Si consiglia di garantire la sua web.config o inserito la chiave API Crypteron (AppSecret) programmaticamente (documentation)

Puoi trovare le app di esempio su GitHub allo https://github.com/crypteron/crypteron-sample-apps. .

A proposito, la versione gratuita beneficia delle offerte commerciali, quindi oltre a quanto sopra, è anche possibile proteggere flussi, file, oggetti, code di messaggi, database NoSQL, ecc., Tutto da un'unica posizione.

Disclaimer: Lavoro lì e abbiamo una versione gratuita della community che chiunque può utilizzare (e non facciamo soldi). Se pensi che sia bello, dicci, dillo ai tuoi amici. Se hai un budget, ottieni una licenza commerciale. Ci aiuta a offrire l'edizione gratuita a tutti :)