[英]How can I plot more than 50,000 values in a scatter chart, saving computer resource?
[英]How to search on a table with 50,000 records that are encrypted
我有SQL Server 2012,但無法遷移到SQL Server 2016。
我正在通過Entity Framework Code First以這種方式使用加密。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace x.y.Api.Models
{
[Table("Tbl_Naturalezas")]
public class Naturalezas: EncryptDecrypt
{
public Naturalezas()
{
_locked = true;
}
[Key]
public int idNaturaleza { get; set; }
string _naturaleza;
[StringLength(350)]
public string naturaleza
{
get { return locked ? Decrypt(_naturaleza, ConfigurationManager.AppSettings["appKeyPassword"]) : naturaleza; }
set { _naturaleza = IsEncrypted(value) ? value : Encrypt(value, ConfigurationManager.AppSettings["appKeyPassword"]) ; }
}
public virtual ICollection<Contactos> Contactos { get; set; }
}
}
從此類繼承:
public class EncryptDecrypt
{
public bool _locked;
public const string EncryptedStringPrefix = "X";
private const int Keysize = 256;
private const int DerivationIterations = 1000;
public string Encrypt(string atributoClase, string passPhrase)
{
string plainText = atributoClase.ToUpper();
if (plainText != null)
{
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
else
{
return plainText;
}
}
public string Decrypt(string atributoClase, string passPhrase)
{
string cipherText = atributoClase.ToUpper();
if (cipherText != null)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
else
{
return cipherText;
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
public void Lock()
{
_locked = true;
}
public void Unlock()
{
_locked = false;
}
public bool IsEncrypted(string atributosClases)
{
if (atributosClases != null)
{
if(atributosClases.Length > 50)
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
}
在POST api控制器中,我這樣做:
// POST: api/Naturalezas
[ResponseType(typeof(Naturalezas))]
public IHttpActionResult PostNaturaleza(Naturalezas naturaleza)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
naturaleza.Unlock();
db.Naturalezas.Add(naturaleza);
db.SaveChanges();
naturaleza.Lock();
return CreatedAtRoute("DefaultApi", new { id = naturaleza.idNaturaleza }, naturaleza);
}
我基於此博客文章進行了加密:
現在,這僅用於一個表,但是在另一個表中,我們有20個字段,並且所有字段都必須加密,但是我們需要能夠使用LIKE,=等在這20個字段上進行搜索。
什么是最好的解決方案(將我引向代碼解決方案),能夠:
當前正在使用中間層解決方案加密靜態數據時,您需要做出很多權衡。 盡管Always Encrypted確實使事情變得更加輕松,並且消除了應用程序中的自定義加密代碼,但是自定義加密仍然存在類似的局限性,例如無法進行通配符LIKE
過濾,因為它們的功能相似,而加密/解密不是發生在數據庫級別。
一些建議:
基本過濾仍然有效當使用相同的加密密鑰和加密鹽時,您仍然可以執行常規WHERE x = 'y'
類型的過濾。
將搜索和過濾轉移到中間層
同樣,需要權衡取舍並可能影響性能,但是一旦解密了數據,就可以使用普通的舊LINQ執行更復雜的過濾
您真的需要加密該列嗎?
您的數據沒有歸類為PHI,PII或類似的東西嗎? 考慮不對其進行加密,您可以執行常規的SQL WHERE
和LIKE
過濾
這些問題可能會導致各種正確答案。 我只會說說我將對每個問題做什么
1.加密數據庫上的所有字段。
我認為這個問題可以分為兩個部分:
這部分是最簡單的(但不是最短的),可以通過一個簡單的oneshot項目解決,以讀取每一行並將其更新為加密格式。 這部分是可選的,因為您的項目會將加密數據與明文數據區分開。
這里有兩個選擇,具體取決於您要花多少時間來解決第一個問題。 最好的辦法是更改從EF項目保存數據的方式。 最壞的情況是在給定的時間重新運行項目A部分。
2.進行搜索。
最簡單,最安全的方法是加載所有數據並使用Linq請求。
3.保持性能。
他們有很多解決方法,這取決於您來自Web項目還是軟件項目。 我將只討論可能涉及到兩者的解決方案。
但是要當心! 如果您不特別注意這一點,則下面的每個解決方案都會增加很多安全問題。 實施許多要求更多的修補工作。
最好的通用解決方案之一是擁有一些$ cache $。 它可以使用SqlChangeMonitor
( 示例 )直接緩存數據庫,或者使用Entity Framework Extended從EF項目中緩存一些時間。
也許您可以同時使用SqlChangeMonitor
來更新EF緩存。
示例:您的columnA,B和C的數據按其每個可能的值均勻分布。 將這些列與索引一起使用,並在每個Where
都可以快速響應。
如何實現:使用內部選擇或返回表並僅從結果中查詢的函數制作存儲過程。 您的EF項目將需要重新設計,以便從存儲過程的功能/結果集中進行查詢。
在登錄/進入時,您的用戶可以將所有數據保存到帶有未加密數據的臨時索引表中,並在該臨時表上進行查詢。 如果您開發了解決方案1.A,這將很容易。 您可以使用“ Keep Fixed Plan
或“ Keep Plan
選項以提高性能。 再次,它會要求您重新設計您的EF項目(但這應該更簡單)。
警告:請勿使用全局臨時表。 這將破壞具有加密數據的意義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.