Обеспечение безопасности ключа шифрования symri c в памяти - PullRequest
2 голосов
/ 06 мая 2020

У меня есть приложение, в котором я извлекаю ключ шифрования c, имеющийся на диске, и использую его для шифрования данных. Ключ шифрования извлекается с диска при запуске программы и сохраняется в виде массива байтов в переменной частного класса. Сразу после извлечения ключа с диска при запуске программы ProtectedMemory.Protect() используется для защиты ключа. Ключ не защищен ProtectedMemory.Unprotect() каждый раз, когда его нужно использовать, и снова защищается после использования.

Части, которые заставили меня задуматься об эффективности этой схемы, происходят в тех случаях, когда ключ извлекается с диска, и каждый раз, когда необходимо использовать ключ, поскольку уязвимость, которую можно легко использовать, создается в течение 2 ключевых моментов цикла выполнения программы: когда программа только что завершила загрузку ключа с диска и не вызвала метод Protect() и когда ключ не защищен для использования во время шифрования.

class ApplicationClass {
   private byte[] encKey;

   public ApplicationClass() {
     // Fetches the encryption key first
     encKey = StorageInt.FetchKey(); // Fetches and returns the encrypted key from the disk

     // A gaping vulnerability here as the key is just loaded in memory and is not protected
     ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);

     // Other initialization instructions follows
   }

   private byte[] ApplySymmEnc(byte[] plaintext) {
     Aes aes = Aes.Create();
     byte[] iv = new byte[128];
     RNGCryptoServiceProvider randomBytesGenerator = new RNGCryptoServiceProvider();
     randomBytesGenerator.GetNonZeroBytes(iv);
     randomBytesGenerator.Dispose();
     ProtectedMemory.Unprotect(encKey, MemoryProtectionScope.SameProcess);

     // Another gaping vulnerability here!

     ICryptoTransform encryptor = aes.CreateEncryptor(encKey, iv);
     ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess); // Protect the key right after it is used for encryption

     // Instructions for encryption follows
   }
}

Заранее спасибо.

РЕДАКТИРОВАТЬ: Что касается причины, по которой не заботится о безопасности ключа при нахождении на диске, ключ существует на диске в достаточно защищенной обфусцированной форме, которая расшифровывается функцией StorageInt.FetchKey() при извлечении.

Ответы [ 3 ]

3 голосов
/ 06 мая 2020

Защита от всех атак невозможна. Самое близкое, что вы могли бы получить, это использовать чип Trusted Platform Module (tpm), поэтому ключ никогда не покидает чип. Вторым лучшим вариантом может быть использование доверенной среды выполнения , если таковая предлагается вашим процессором. Но ни один из них не защищен от всех видов атак.

Если выделенная аппаратная поддержка недоступна, вероятно, нецелесообразно пытаться защитить вашу программу от администратора. Если злоумышленник может прочитать память вашей программы, почему он не может просто прочитать ключ с диска?

Шифрование ключа в оперативной памяти полезно для защиты от таких вещей, как атаки холодной загрузки, и такие атаки, вероятно, будут трудно вовремя получить ключ в уязвимый момент.

Есть также некоторые ответы на вопрос лучшие практики для ключей в памяти , которые могут быть полезны.

0 голосов
/ 24 мая 2020

Сохранение симметричных c ключей шифрования, защищенных в это время (после выборки с диска, но перед вызовом ProtectedMemory.Protect), потребует поддержки уровня ОС, которой нет в Windows (и других. net основных платформах. ).

Непосредственно от команды. net по аналогичной c работе с учетными данными: «Цель SecureString - избежать хранения секретов в памяти процесса в виде простого текста. ... Однако , даже на Windows SecureString не существует как концепция ОС ... Он просто делает окно, в котором простой текст становится короче ... Общий подход к работе с учетными данными состоит в том, чтобы избегать их и вместо этого полагайтесь на другие средства аутентификации, такие как сертификаты или Windows аутентификация "(https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md)

0 голосов
/ 15 мая 2020

Вы упомянули

", когда программа только что завершила загрузку ключа с диска и не вызвала Protect()"

Если я перефразирую Приведенное выше предложение StorageInt.FetchKey() извлекает незащищенный ключ, и вы хотите защитить его на этом этапе.

Вы можете создать метод расширения для StorageInt.FetchKey(bool IsProtectKey = true), этот метод может вызывать ProtectedMemory.Protect(). Для получения ключа можно использовать только метод расширения.

     public int StorageInt.FetchKey(bool IsProtectKey = true)
        {
            encKey = StorageInt.FetchKey();
            CodeEncryptedKey = EncryptAndStoreProtectedKey(); 
            //Above function should encrypt your "encKey" to AES256 or to any secure encryption algorithm, store it in cache and return encrypted key

            ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);            
        }

        Public string UseKey(string CodeEncryptedKey)
        {
            encKey = GetProtectedKeyFromCache(CodeEncryptedKey)
            ProtectedMemory.Unprotect(encKey, MemoryProtectionScope.SameProcess);
            Task.Run(() => ProtectKeyAfter5Seconds(encKey));
            return encKey;
        }

        Void ProtectKeyAfter5Seconds(encKey)
        {
            Thread.Sleep(5000); //Im telling here to encrypt after 5 seconds, you can have your logic to encrypt after one time use or any particular logic
            ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);
        }

Это помогает?

...