19 смертных грехов, угрожающих безопасности программ
Шрифт:
Сначала посмотрим, как нам может помочь ОС.
Использование технологий защиты, предоставляемых операционной системой
Во время работы над этой книгой только 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
В 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);
}
Вот как реализована функция SecureZeroMemory в Windows:
FORCEINLINE PVOID SecureZeroMemory(
void *ptr, size_t cnt) {
volatile char *vptr = (volatile char *)ptr;
while (cnt) {
*vptr = 0;
vptr++;
cnt—;
}
return ptr;
}
А вот другая реализация, которую предложил Дэвид Уилер (см. раздел «Другие ресурсы»):
void guaranteed memset(void *v, int c, size t n)
{ volatile char *p=v; while(n-) *p++=c; return v; }
Искупление греха bASP.NET версии 1.1 и старше
Показанное ниже решение применимо к Web–приложениям, написанным на ASP.NET версии 1.1 и старше. Поскольку многие Web–приложения обращаются к базе данных, команда, работавшая над ASP.NET, постаралась максимально облегчить безопасное хранение секретной информации (скажем, строк соединения с сервером) в файле web.config. Подробнее см. статью в базе знаний Q329290 (ссылка приведена в разделе «Другие ресурсы»). Эта методика основана на использовании DPAPI.
Для хранения пароля в конфигурационном файле можно также обратиться к методу HashPasswordForStoringlnConfigFile.
Искупление греха в С# на платформе . NET Framework 2.0
В первом примере
показано, как получить пароль, а потом записать защищенный пароль в файл. Отметим, что DPAPI позволяет защитить данные так, что они будут доступны либо только текущему пользователю, либо всем приложениям на данной машине. Что именно больше подходит для вашего приложения, определяется моделью угроз.byte[] sensitiveData = Encoding.UTF8.GetBytes(GetPassword);
byte[] protectedData = ProtectedData.Protect(sensitiveData, null,
DataProtectionScope.CurrentUser);
FileStream fs = new FileStream(filename, FileMode.Truncate);
fs.Write(protectedData, 0, protectedData.Length);
fs.Close;
Ниже продемонстрирована обратная процедура: файл открывается, и из него читаются секретные данные:
FileStream fs = new FileStream(filename, FileMode.Open);
byte[] protectedData = new byte[512];
fs.Read(protectedData, 0, protectedData.Length);
byte[] unprotectedBytes = ProtectedData.Unprotect(protectedData, null,
DataProtectionScope.CurrentUser);
fs.Close;
Примечание. Если на платформе .NET Framework вы храните пароли в строках типа String, то лучше бы воспользоваться классом SecureStr ing. См. ссылку на статью «Making Strings More Secure» в разделе «Другие ресурсы».
Искупление греха в C/C++ для Mac OS X версии v10.2 и старше
На страницеadd.с есть пример, показывающий, как добавить пароль или ключ к «брелку» Key–chain на компьютерах фирмы Apple. Используются следующие основные функции:
// Установить пароль
SecKeychainRef keychain = NULL; // пользовательская цепочка ключей
// по умолчанию
OSStatus status = SecKeychainAddGenericPassword(keychain,
strlen(serviceName), serviceName,
strlen(accountName), accountName,
strlen(passwordData), passwordData,
NULL);
if (status == noErr) {
// все хорошо!
}
// Получить пароль
char *password = NULL;
u_int_32_t passwordLen = 0;
status = SecKeychainFindGenericPassword(keychain,
strlen(serviceName), serviceName,
strlen(accountName), accountName,
&passwordLen, &password,
NULL);
if (status == noErr) {
// все хорошо! Используем пароль
// Прибрать за собой
guaranteed_memset(password,42,passwordLen);
SecKeychainItemFreeContent(NULL, (void*) password);
}
Искупление греха без помощи операционной системы (или «храните секреты от греха подальше»)
Это посложнее. Конечно, лучше поручить всю трудную работу операционной системе, но если целевая ОС не готова помочь вам спрятать секрет, придется создать собственный механизм. Проще всего убрать секретные данные с линии огня.
Мы уже отмечали выше, что всегда надо думать, от кого вы защищаетесь и какова ценность защищаемых данных. Если вы работаете над Web–приложением, которое должно защищать некоторую секретную информацию, то ее следует разместить вне «Web–пространства». Иными словами, если приложение находится в каталоге , то сохраните секретные данные в , а еще лучше в , поскольку эти каталоги оказываются за линией огня. С другой стороны, до каталога wwwroot (и его подкаталогов) можно добраться из браузера. Разумеется, ваш сервер не должен возвращать клиенту текстовые конфигурационные файлы (к примеру, web.config, app.config и global.asa в случае IIS или httpd.conf или .htaccess в случае Apache), но достаточно небольшой ошибки в Web–приложении или Web–сервере – и противник сможет прочитать секретные данные.
В Windows можно также использовать реестр, тогда противнику придется как–то исполнить на серверной машине код, который может читать значения из реестра.
Если вы работаете с сервером Apache в Linux, Mac OS X и UNIX, то не стоит хранить секретные конфигурационные данные в каталоге, на который указывает параметр DocumentRoot (он определен в файле httpd.conf). Например, в дистрибутивах Red Hat и Fedora Core это /var/www/html. То же относится и к каталогу cgi–bin.
В примерах ниже показано, как читать секретные данные из ресурса, недоступного через Web.