使用ASP.NET和C#进行MediCare卡加密

时间:2012-04-24 01:24:10

标签: c# asp.net security encryption

我正在为一个非盈利组织提供免费咨询,这个组织想要一个简单的系统来管理具有日程安排组件等的多个组,并且还管理孩子们的医疗信息。它全部在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与值一起存储。例如,当我希望它在文本框中解密时,它可以正常工作。

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

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

解密密码没有存储在数据库中,我是在purpuse上完成的。

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

我已经设置了更多的安全性问题,因为SSL将在网站上激活,因此信息会被加密并防止掉线。

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

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

感谢您抽出宝贵时间阅读本文

5 个答案:

答案 0 :(得分:1)

回答主要问题:

  

我只是不确定我是否遵循这里的最佳做法,可以   如果物理服务器被盗,信息很容易被解密?

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

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

  

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

不一定。别介意你必须将密码存储在某个地方。如果他们窃取了你的数据库服务器,那么他们也可能会带走网络服务器(也有凭据)。

  

我们有时需要在紧急情况下解密它。

关于"紧急情况"解密:有两种方法可以解决这个问题。要么将密码的副本存储在您可以访问它的地方,要么将其破解。

  

我的解密方法是否安全?

我会称你的方法浪费时间。有关详细信息,请参阅下文。


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

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

对于"运动中的数据"你应该打开kerberos进行Web服务器和数据库服务器之间的通信。 (即:不要相信某人没有尝试监听网络和数据库服务器之间的连接。也不要相信路由器配置正确......他们很少。)阅读有关如何正确配置AD环境的信息。您还应该启用SSL以进行Web服务器和客户端桌面之间的通信。

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

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

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

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

用细齿梳子完成你的代码。阅读OWASP。消除你的输入。所有这些。确保所有数据库查询都传递参数,并且您不能动态构建任何内容。禁用SA帐户。

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

答案 1 :(得分:0)

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

RijndaelManaged Encryptation

答案 2 :(得分:0)

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

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

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

  • 从内部保护。 您的用户的安全意识如何?他们是否将密码存储在键盘下,共享登录信息或在屏幕未锁定时离开。如果其中任何一个是肯定的,您可能需要对UI设计提出一些想法。链条只有最薄弱的环节才有力量。

答案 3 :(得分:0)

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

  • 我不太了解1& 1,但“where”也可能是一个问题(?) - 其中是服务器(1& 1)的物理位置?

  • 还有另外一个想法:不是用户生成salt / pwd(它们应该有多久/安全?)或许查看SQL server encryption options,将作业移动到SQL而不是用户和/或代码。

  • 决定数据是否应加密或散列(存储/检索与查找) - re:password = hashed,SSN - 取决于我猜的用法。 Hash can also be done in SQL Server ....

答案 4 :(得分: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);
}