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

Мне нужно написать службу 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');
}

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

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

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

Ответы [ 6 ]

34 голосов
/ 24 сентября 2010

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

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

9 голосов
/ 24 сентября 2010

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

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

8 голосов
/ 24 сентября 2010

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

1 голос
/ 24 сентября 2010

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

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

0 голосов
/ 24 сентября 2010

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

#pragma optimize( "", off )

// Code, not to optimize goes here

#pragma optimize( "", on )

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

0 голосов
/ 24 сентября 2010

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

volatile std::string password = getPassword(); // Get the password from the user
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...