web-dev-qa-db-ja.com

ASP.NETのC#でメディケアカード番号を暗号化する

複数のグループをスケジュールコンポーネントなどで管理し、子どもたちの医療情報も管理するシンプルなシステムを求めている特定非営利活動法人に無料相談をしています。すべてがExcelファイルにありますが、問題は、看護師にとってあまり便利ではないことです。そのため、私はこのASP.NETアプリケーションを構築しており、機密データの暗号化を試みると壁にぶつかります。このC#コードは、このWebサイトの書籍や推奨事項に従って作成しました。私はそれを正しくやっていますか?

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

    protected void Page_Load(object sender, EventArgs e)
    {

    }


    /// <summary>
    /// Handles the OnClick event of the Encrypt control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    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);


    }

    /// <summary>
    /// Handles the OnClick event of the Decrypt control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    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);

    }

    /// <summary>
    /// Generates a cryptographically secure block of data suitable for salting hashes.
    /// </summary>
    /// <returns>A cryptographically secure block of data suitable for salting hashes.</returns>
    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;
    }

    /// <summary>
    /// Generates a key and IV from password and salt.
    /// </summary>
    /// <param name="password">The password.</param>
    /// <param name="salt">The salt used in generation.</param>
    /// <param name="symmetricAlgorithm">The symmetric algorithm to generate the key and IV for.</param>
    /// <param name="key">The generated key.</param>
    /// <param name="iv">The generated IV.</param>
    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);
    }
}

コードは、データを入力すると機能します。それはデータベースで暗号化され、適切なソルトが値とともに保存されます。テキストボックスで解読したい場合は、正しく動作します。

私の考えは:看護師はフォームにデータを入力し、パスワードで暗号化し、暗号化されたMediCare番号とソルト値がサーバーによって生成されてSQLサーバーに送信され、MediCareは次のようなテーブルで暗号化されます。

Medicare_ID - int (Primary Key)
Medicar_Number - (varchar(MAX))
Medicare_SALT - (varchar(MAX))

復号化パスワードはデータベースに保存されていません。私は故意にこれを行いました。

ここでベストプラクティスに従ったかどうかはわかりません。物理サーバーが盗まれた場合、私の情報は簡単に復号化できますか?彼らはまだデータを復号化するためにパスワードが必要ですよね?緊急の場合は、復号化する必要がある場合があります。私の解読方法は安全ですか?もう1つの懸念は、1と1の共有ウェブホスティングを使用していることです。

さらにいくつかのセキュリティ対策を設定しました。たとえば、SSLがWebサイトでアクティブになるので、情報が暗号化され、目をつぶらないようにします。

念のため、接続文字列と入力が無害化されるようにしますが、理論的には、1〜2人のマネージャーまたは看護師のみがデータを操作します。

Webサイトへのアクセスは、Visual Studio 2010から借用したログインコントロールのユーザー名とパスワードで保護されています。

4
metraon

暗号化についてのいくつかの批判: パスワードから暗号化キーを生成しないようにする必要があります (または、絶対に必要な場合は、反復回数の多いPBKDF2を使用する必要がありますが、結果のスキームはおそらく不十分であることを理解してくださいセキュリティ、および誤った安心感を提供する場合があります)、および 暗号化されたデータにMACを適用する必要があります 。暗号の詳細(MediCare番号ビットなど)には従いませんでした。一般に、 自分で暗号を適切に使用する方法を理解する よりも、おそらくデータを暗号化するためにGPGを使用する方が安全でしょう。

しかし、暗号を超えて-

1人の看護師だけがデータを解読できるようにデータを暗号化することが、医療現場にとって正しいアプローチであるかどうかはわかりません。誰かが緊急時に医療データにアクセスする必要があり、一人の看護師が近くにいない場合はどうなりますか?おそらく、アクセスをそれほど厳しく制限する代わりに、医療従事者がデータにアクセスできるようにする「緊急時の対応策」を用意する必要があります。強力な監査と監査ログの精査を組み合わせて、 「ガラスを割る」機能。

Webサービスへの医療データの保存には注意してください。医療データが誤ってクライアント側に保存されないように(リークされる可能性があります)。クライアントがそれをキャッシュしないように、キャッシュしないヘッダーを送信することを確認してください。サイト全体でSSLを使用します。また、機密データをPDFまたはクライアントのマシンにダウンロードされる他の形式で利用できるようにしないでください。

このシステムがHIPAAに準拠する必要があるかどうかを確認しましたか?

3
D.W.