Подтвердить что ты не робот

Как убедиться, что оптимизация компилятора не представляет угрозы безопасности?

Мне нужно написать службу Windows, которая обрабатывает некоторые конфиденциальные данные (например, PIN-коды, пароли и т.д.). Эти сведения необходимы в течение очень короткого промежутка времени: обычно они отправляются почти сразу в устройство для чтения смарт-карт.

Давайте рассмотрим этот фрагмент кода:

{
  std::string password = getPassword(); // Get the password from the user

  writePasswordToSmartCard(password);

  // Okay, here we don't need password anymore.
  // We set it all to '\0' so it doesn't stay in memory.
  std::fill(password.begin(), password.end(), '\0');
}

Теперь я беспокоюсь о оптимизации компилятора. Здесь компилятор может обнаружить, что пароль вот-вот будет удален, и что изменение его значения в этот момент бесполезно и просто удалит вызов.

Я не ожидаю, что мой компилятор будет заботиться о ценности будущей неохраняемой памяти.

Является ли моя озабоченность законной? Как я могу быть уверенным, что такой фрагмент кода не будет оптимизирован?

4b9b3361

Ответ 1

Да, ваши проблемы законны. Вам нужно использовать специально разработанную функцию, например SecureZeroMemory(), чтобы предотвратить изменение настроек поведения кода.

Не забывайте, что класс строк должен быть специально разработан для обработки паролей. Например, если класс перераспределяет буфер для хранения более длинной строки, он должен стереть буфер перед его перенастройкой в ​​распределитель памяти. Я не уверен, но, скорее всего, std::string не делает этого (по крайней мере, по умолчанию). Использование неподходящего класса обработки строк делает все ваши проблемы бесполезными - у вас будет пароль, скопированный на всю память программы, которую вы даже знаете.

Ответ 2

Это проблематично, но по другой причине. Кто сказал, что std::string password = getPassword(); не оставляет в памяти еще одну копию? (Вероятно, вам нужно написать "защищенный" класс распределителя для этого, чтобы память нулей "уничтожить" или "освободить" )

В своём спокойном коде вы можете избежать оптимизации, получив volatile указатель на строковые данные (я не знаю, если вы можете сделать это стандартным образом), а затем обнулите данные.

Ответ 3

Не используйте std::string для паролей, так как он не обнуляет память при перераспределении или уничтожении - вместо этого создайте собственный класс ConfidentialString. При разработке этого класса вы можете воспользоваться CryptProtectMemory... и быть очень осторожным, когда вам нужно использовать расшифрованную версию, особенно при вызове внешнего кода.

Ответ 4

В этом конкретном случае я был бы очень удивлен, если компилятор сможет оптимизировать вызов метода, который мог бы иметь побочные эффекты. Или std:: fill inline, чтобы компилятор мог увидеть реализацию? (Я не программист на С++).

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

Ответ 5

Почему бы вам просто отключить оптимизацию для рассматриваемого кода?

#pragma optimize( "", off )

// Code, not to optimize goes here

#pragma optimize( "", on )

Этот пример оптимизации #pragma специфичен для MSVC, но другие компиляторы также поддерживают его.

Ответ 6

Объявить пароль volatile, чтобы компилятор не делал никаких предположений об удалении явного чтения или записи.

volatile std::string password = getPassword(); // Get the password from the user