19 смертных грехов, угрожающих безопасности программ
Шрифт:
CAN–2002–1590
Цитата из бюллетеня CVE: «Система управления предприятием из Web (Web Based Enterprise Management – WBEM) для Solaris 8 с обновлением 1/01 или более поздним инсталлирует пакеты SUNWwbdoc, SUNWwbcou, SUNWwbdev и SUNWmgapp с правом записи для группы и мира. Это позволяет локальному пользователю вызвать отказ от обслуживания или расширить свои привилегии».
Более подробную информацию об этой проблеме можно найти на страницеили по адресу www.securityfocus.com/bid/6061/.
CVE–1999–0886
Цитата из бюллетеня CVE: «Дескриптор защиты для RASMAN позволяет пользователю указать на другое место с помощью Windows NT Service Control Manager ».
Дополнительную
CAN–2004–0311
Программа Web/SNMP Management для управления устройством SmartSlot Card AP9606 AOS компании American Power Conversion версий 3.2.1 и 3.0.3 поставляется с зашитым паролем по умолчанию. Локальный или удаленный противник, имеющий возможность установить Telnet–соединение с устройством, может указать произвольное имя пользователя и пароль «TENmanUFactOryPOWER» и получить неавторизованный доступ к устройству.
CAN–2004–0391
Согласно бюллетеню безопасности Cisco на странице www.cisco.com/warp/ public/707cisco–sa–20040407–username.shtml:
Стандартная пара «имя/пароль» зашита во всех версиях программ Wireless LAN Solution Engine (WLSE) и Hosting Solution Engine (HSE). Пользователь, зашедший под этим именем, получает полный контроль над устройством. Это имя нельзя деактивировать. Способов обойти проблему не существует.
Искупление греха
Слабый контроль доступа – это, по большей части, проблема уровня проектирования. А лучшим способом решения проектных проблем является построение модели угроз. Тщательно рассмотрите все объекты, создаваемые вашим приложением на этапе инсталляции и во время работы. Один из лучших специалистов по анализу кода в Microsoft говорит, что большую часть ошибок он находит «с помощью notepad.exe и собственных мозгов». Поэтому работайте головой!
Следующий по сложности шаг к искуплению греха – изучить платформу, для которой вы пишете, и понять, как на ней работают подсистемы защиты. Вы должны (повторяем – должны!) разбираться в механизме управления доступом на целевой платформе.
Один из способов не попасть в беду заключается в том, чтобы провести различие между данными уровня системы и отдельного пользователя. Если вы это сделаете, то установить правильные ограничения доступа будет совсем несложно, обычно можно будет принять предлагаемые системой умолчания.
А теперь перейдем к самой важной теме – устранению «зашитых» секретов. Изжить этот грех не всегда бывает просто; если бы это было легко, то никто бы и не грешил! Существует два потенциальных лекарства:
□ использовать методы защиты данных, предоставляемые операционной системой;
□ переместить секретные данные туда, где они не принесут вреда.
Мы рассмотрим оба способа подробно, но прежде приведем одно очень важное изречение:
Программа не может защитить сама себя.
Злонамеренный пользователь с неограниченным доступом к компьютеру, обладающий достаточно обширными знаниями, может раскрыть любые секреты, особенно если он является администратором.
Поэтому вы обязательно должны решить, от кого защищаете систему, а затем оценить, достаточно ли прочна защита с учетом важности той информации, которую вы желаете сохранить в секрете. Защитить закрытый ключ, используемый
Сначала посмотрим, как нам может помочь ОС.
Использование технологий защиты, предоставляемых операционной системой
Во время работы над этой книгой только Windows и Mac OS X обладали развитыми средствами для хранения секретных данных. Именно ОС решает критически важную (и трудную) задачу управления ключами. В Windows для этой цели служит Data Protection API (DPAPI), а в Mac OS X – технология KeyChain.
Воспользоваться DPAPI очень просто из любого языка, имеющего средства для доступа к внутренним структурам Windows. Полное объяснение принципов работы этого механизма можно найти на сайте(точная ссылка приведена в разделе «Другие ресурсы»).
Ниже показано, как обратиться к низкоуровневому API из программы на C/C++, а также – на примере С# – из управляемого кода на платформе .NET Framework 2.0.
Примечание. В .NET 1.x нет классов для обращения к DPAPI, но существует много оберток. См. пример в книге Майкла Ховарда, Дэвида Леб–ланка «Защищенный код», 2–ое издание (Русская редакция, 2004).
В Windows имеется также интерфейс Crypto API (CAPI) для доступа к ключам шифрования. Вместо того чтобы использовать ключ непосредственно, вы передаете описатель скрытого внутри системы ключа. Эта методика также рассмотрена в книге «Защищенный код», 2–ое издание.
Искупление греха в C/C++для Windows 2000 и последующих версий
Приведенный ниже код иллюстрирует, как обращаться к DPAPI из программ на языках C/C++ для Windows 2000 и более поздних версий. В этом коде есть две функции, которые вы должны реализовать сами: одна возвращает статический указатель типа BYTE* на секретные данные, а другая – статический указатель типа BYTE* на данные, вносящие дополнительную энтропию. В самом конце программа вызывает функцию SecureZeroMemory, чтобы стереть данные из памяти. Используется именно эта функция, а не memset или Zer oMemory, так как последние могут быть устранены из двоичного кода компилятором в ходе оптимизации.// Защищаемые данные
DATA_BLOB blobIn;
blobIn.pbData = GetSecretData;
blobIn.cbData = lstrlen(reinterpret_cast<char *>(blobIn.pbData)) + 1;
// Дополнительная энтропия, которую возвращает внешняя функция
DATA_BLOB blobEntropy;
blobEntropy.pbData = GetOptionalEntropy;
blobEntropy.cbData = lstrlen(reinterpret_cast<char *>(blobIn.pbData));
// Зашифровать данные
DATA_BLOB blobOut;
if(CryptProtectData(
&blobIn,
L"Sin#12 Example", // необязательный комментарий
&blobEntropy,
NULL,
NULL,
0,
&blobOut)) {
printf("Защита сработала.\n");
} else {
printf("Ошибка при вызове CryptProtectData -> %x", GetLastError);
exit(-1);
}
// Дешифрировать данные
DATA_BLOB blobVerify;
if(CryptUnprotectData(
&blobOut,
NULL,
&blobEntropy,
NULL,
NULL,
0,
&blobVerify)) {
printf("Расшифрованные данные: %s\n", blobVerify.pbData);
} else {
printf("Ошибка при вызове CryptUnprotectData -> %x",
GetLastError);
exit(-1);
}
if (blobOut.pbData)
LocalFree(blobOut.pbData);
if (blobVerify.pbData) {
SecureZeroMemory(blobOut.pbData, blobOut.cbData);
LocalFree(blobVerify.pbData);
}