1

我正在为一个非营利组织提供免费咨询,他们想要一个简单的系统来管理具有日程组件等的多个组,并管理孩子的医疗信息。它都在一个excel文件上,但这个问题对护士来说不是很方便。所以我正在构建这个 asp.net 应用程序,当它涉及敏感数据的加密时,我碰壁了。我根据本网站的书籍和建议编写了这个 c# 代码。我做得对吗?

 using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web.UI.WebControls;

public partial class SymmetricEncryptionWithPassword : System.Web.UI.Page
{

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void Encrypt_OnClick(object sender, EventArgs e)
    {
        // Initialize the algorithm from the values on the page.
        SymmetricAlgorithm symmetricAlgorithm = new AesManaged();

        byte[] generatedKey = null;
        byte[] generatedIV = null;

        TextBox salt = (TextBox)DetailsView1.FindControl("TextBox2");
        TextBox ssn = (TextBox)DetailsView1.FindControl("TextBox1");

        salt.Text = Convert.ToBase64String(GenerateSalt());

        GetKeyAndIVFromPasswordAndSalt(this.password.Text, Convert.FromBase64String(salt.Text), symmetricAlgorithm, ref generatedKey, ref generatedIV);

        symmetricAlgorithm.Key = generatedKey;
        symmetricAlgorithm.IV = generatedIV;

        ICryptoTransform encryptor = symmetricAlgorithm.CreateEncryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);

        // Create the streams used for encryption.
        MemoryStream memoryStream = new MemoryStream();
        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
        {
            byte[] plainTextAsBytes = new UTF8Encoding(false).GetBytes(ssn.Text);
            cryptoStream.Write(plainTextAsBytes, 0, plainTextAsBytes.Length);
        }

        symmetricAlgorithm.Clear();

        byte[] encryptedData = memoryStream.ToArray();
        ssn.Text = Convert.ToBase64String(encryptedData);


    }


    protected void Decrypt_OnClick(object sender, EventArgs e)
    {
        // Initialize the algorithm from the values on the page.
        SymmetricAlgorithm symmetricAlgorithm = new AesManaged();

        byte[] generatedKey = null;
        byte[] generatedIV = null;

        TextBox txt = (TextBox)DetailsView1.FindControl("TextBox2");
        TextBox txt1 = (TextBox)DetailsView1.FindControl("TextBox1");
        GetKeyAndIVFromPasswordAndSalt(this.password.Text, Convert.FromBase64String(txt.Text), symmetricAlgorithm, ref generatedKey, ref generatedIV);

        symmetricAlgorithm.Key = generatedKey;
        symmetricAlgorithm.IV = generatedIV;

        ICryptoTransform decryptor = symmetricAlgorithm.CreateDecryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);

        // Create the streams used for encryption.
        MemoryStream memoryStream = new MemoryStream();
        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
        {
            byte[] encryptedDataAsBytes = Convert.FromBase64String(txt1.Text);
            cryptoStream.Write(encryptedDataAsBytes, 0, encryptedDataAsBytes.Length);
        }

        symmetricAlgorithm.Clear();

        byte[] decryptedData = memoryStream.ToArray();

        txt1.Text = Encoding.UTF8.GetString(decryptedData);

               }


    private static byte[] GenerateSalt()
    {
        const int MinSaltSize = 8;
        const int MaxSaltSize = 16;

        // Generate a random number to determine the salt size.
        Random random = new Random();
        int saltSize = random.Next(MinSaltSize, MaxSaltSize);

        // Allocate a byte array, to hold the salt.
        byte[] saltBytes = new byte[saltSize];

        // Initialize the cryptographically secure random number generator.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        // Fill the salt with cryptographically strong byte values.
        rng.GetNonZeroBytes(saltBytes);

        return saltBytes;
    }


    private static void GetKeyAndIVFromPasswordAndSalt(string password, byte[] salt, SymmetricAlgorithm symmetricAlgorithm, ref byte[] key, ref byte[] iv)
    {
        Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt);
        key = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.KeySize / 8);
        iv = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.BlockSize / 8);
    }
}
}

该代码有效,当我输入数据时,它在数据库中加密,并且正确的 Salt 与值一起存储。例如,当我希望它在文本框中解密时,它可以正常工作。

我的想法是:护士在表格中输入数据,她用密码对其进行加密,服务器生成加密的医疗保险号码和盐值,然后发送到 SQL 服务器,医疗保险在看起来像这样的表中加密.

Medicare_ID - int(主键)
Medicar_Number - (varchar(MAX))
Medicare_SALT - (varchar(MAX))

解密密码没有存储在数据库中,我是故意这样做的。

我只是不确定我是否遵循这里的最佳实践,如果物理服务器被盗,我的信息是否可以轻松解密?他们仍然需要密码才能解密数据吗?我们有时需要在紧急情况下对其进行解密。我的解密方法安全吗?另一个问题是我使用 1and1 共​​享虚拟主机。

我已经设置了一些更多的安全措施,例如将在网站上激活 SSL,以便对信息进行加密并防止掉眼。

我将确保对连接字符串和输入进行清理以防万一,但理论上只有 1-2 名经理或护士会操纵数据。

网站访问受到用户名密码的保护,该用户名密码带有我从 Visual Studio 2010 借来的登录控件。

感谢您抽时间阅读

4

5 回答 5

1

要回答主要问题:

我只是不确定我是否遵循这里的最佳实践,如果物理服务器被盗,我的信息是否可以轻松解密?

关于被盗服务器:无论你做什么,你都会被盗用。没有已知的安全措施可以完美防止被盗硬件。即使是笔记本电脑中的加密硬盘驱动器也可能被破坏,有时比提供加密服务的公司所相信的要快。哪一个,如果他们已经走上了物理获取硬件的路线,那么他们将做两件事之一:擦除它并转售零件或破解数据。在道德上(有时是合法的),无论最终发生什么,您都应该通知所有客户他们的数据被盗。

我想重点是:加密并不意味着坏人不能窃取数据。它只是意味着它需要一段时间。你加密东西来赢得时间。换锁的时间,通知客户的时间,更改密码的时间;并且,在处理 SSN 和其他有价值的数据时,是时候锁定信用账户了。

他们仍然需要密码才能解密数据吗?

不必要。没关系,您必须将密码存储在某个地方。如果他们窃取了您的数据库服务器,那么他们很可能也带走了 Web 服务器(这将具有凭据)。

我们有时需要在紧急情况下对其进行解密。

关于“紧急”解密:有两种处理方法。要么将该密码的副本存储在您可以访问的地方,要么破解它。

我的解密方法安全吗?

我会称你的方法是浪费时间。有关更多信息,请参见下文。


此时,您应该了解对机器的物理访问等同于对其数据的完全访问。因此,除非您认为您已将其存放在一个真正安全且配备武装警卫的位置,否则您无能为力。典型的数据中心安全性(例如亚马逊云)通常足够好。然而,对于政府数据,一些公司(同样是亚马逊)拥有专门的服务器,专门用于保存您认为需要额外安全性的数据。我怀疑 1 和 1 托管会这样做。

那么,让我们看看有哪些更好的选择。因为你提到了 varchar(max) 我假设你有一个 MS SQL Server。SQL Server 为数据库内置了加密功能。打开这个。如果有人获得了数据库文件的副本(不是服务器,只是文件),那么他们将不得不破解 SQL 的加密来获取数据。我向您保证,您无能为力,而且您不必过多担心密钥管理。这将处理您的“静态数据”。您当然可以再次对其进行加密,但我建议阅读 MS SQL Server 内置的安全性会更好地利用您的时间。

对于“动态数据”,您应该为 Web 服务器和数据库服务器之间的通信打开 kerberos。(即:不要相信有人没有尝试监听网络和数据库服务器之间的连接。也不要相信路由器配置正确。他们很少这样做。)阅读如何正确配置配置您的 AD 环境。您还应该为 Web 服务器和客户端桌面之间的通信打开 SSL。

超越加密,您需要关注两个主要问题。首先是网站对数据库的访问级别。该用户帐户是否基本上具有 SA 级别的权限?如果是这样,你做错了。站点使用的帐户应极其限于仅执行那些绝对必要的项目。换句话说,它不应该允许某人删除或更改表,甚至不应该对他们不应该访问的数据进行全选。这是绝大多数 Web 应用程序失败的地方。程序员认为,唯一能直接进入他们的数据库的就是网络应用程序。现实情况是,将页面等注入现有应用程序有时太容易了。作为旁注,这是我对大多数 ORM、EF 和 L2S 的主要关注。他们需要太多的访问权限。

我过去常说在网络配置中加密你的连接字符串,但这通常是浪费时间。毕竟,如果他们可以阅读 web.config,那么他们就有足够的访问权限让应用程序吐出它的未加密内容。这使我们回到了网站对数据库的访问级别。坚信除非被证明是无辜的,否则每个人都是有罪的,这是最好的政策。

如果出于某种原因,Web 服务器和数据库服务器在同一个物理机器上:更改此设置。这是一个坏主意,一旦网站被破解,获得对数据库服务器的 sa 级别访问就变得微不足道了(由于大多数网站的构建方式)。

接下来,打开日志记录。在数据库中,您应该至少记录登录失败和成功。在网站上,记录所有内容。另外,关闭从外部对数据库服务器的直接访问。在此期间,继续切换数据库服务器以使用非标准端口(即:非 1433/2433)进行通信。确保数据库服务器防火墙已正确配置为仅允许来自该网络服务器的连接。

用细齿梳检查您的代码。阅读OWASP。清理您的输入。所有这些。确保所有数据库查询都传递参数并且您没有动态构造任何东西。禁用 SA 帐户。

这有点长;但我希望它具有教育意义。

于 2012-04-24T23:24:50.273 回答
0

这是如何使用 RijndaelManaged CryptoServiceProvider 加密和解密敏感信息的一个很好的例子。

Rijndael托管加密

于 2012-04-24T01:59:32.267 回答
0

我不知道确切的答案,但我可以与您分享一些关于如何保护系统的想法。现在任何现有的加密都将在未来被破解。这不是是否的问题,而是何时的问题。话虽如此,您可能想问自己以下问题:

  • 您希望数据安全多长时间?如果您要存储持续 10 年或更短时间的信息,AES-128 就足够了。但如果您将它们存储超过 20 年,请考虑至少使用 AES-256。

  • 如果坏人破解了密码,他们会得到什么?除了加密之外,您还可以使用“隐匿安全”来添加额外的挂锁,例如将加密的字符串/信息保存在两个或多个位置。

  • 从内部保障。您的用户的安全意识如何?他们是否将密码存储在键盘下、共享登录信息或在屏幕未锁定时走开。如果其中任何一项是肯定的,您可能需要对 UI 设计进行一些思考。链条的强度取决于其最薄弱的环节。

于 2012-04-24T05:36:56.763 回答
0
  • 共享主机:检查政府要求,我不在医疗保健领域,但如果没有管理敏感个人信息存储的规则/法律,我会感到非常惊讶。如果信用卡/电子商务有 PCI DSS 要求,我认为医疗保健数据将至少具有相同级别(可能更严格)的数据保护规则(例如HIPAA?)

  • 我对 1&1 了解不多,但“在哪里”也可能是一个问题(?) -服务器(1&1)的物理位置在哪里?

  • 还有一个想法:不要让用户生成 salt/pwd(它们应该多长时间/安全?)也许看看SQL server 加密选项,将工作转移到 SQL 而不是用户和/或代码。

  • 决定数据是否应该加密或散列(存储/检索与查找) - 重新:密码 = 散列,SSN - 取决于我猜的用法。哈希也可以在 SQL Server 中完成....

于 2012-04-24T06:33:36.090 回答
0

此功能是一个不好的做法,IV 需要是随机的以确保安全,相同的 IV 不应该用于相同的密钥。这总是为给定的密钥提供相同的 IV,使其不安全:

private static void GetKeyAndIVFromPasswordAndSalt(string password, byte[] salt, SymmetricAlgorithm symmetricAlgorithm, ref byte[] key, ref byte[] iv)
{
    Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt);
    key = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.KeySize / 8);
    iv = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.BlockSize / 8);
}
于 2012-04-24T21:32:48.870 回答