19 смертных грехов, угрожающих безопасности программ
Шрифт:
Вот как реализована функция 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; }
Искупление
Показанное ниже решение применимо к 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.
Чтение из файловой системы из PHP–сценария в Linux
<?php
$filename = "/home/apache/config", "r";
$fh = fopen($filename);
$data = fread($fh, filesize($filename));
fclose($fh);
?>
Чтение из файловой системы с помощью ASP.NET (С#)
Следующий код читает из app.config имя файла, в котором содержится строка соединения с SQL–сервером. Файл app.config выглядит следующим образом:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="connectFile"
value="c:\\webapps\\config\\sqlconn.config" />
</appSettings>
</configuration>
Остается добавить код на С# для получения значения параметра и последующего чтения строки соединения из файла:
static string GetSQLConnectionString {
NameValueCollection settings = ConfigurationSettings.AppSettings;
string filename = settings.Get("connectFile");
if (filename == null || filename.Length == 0)
throw IOException;
FileStream f = new FileStream(filename, FileMode.Open);
StreamReader reader = new StreamReader(d, Encoding.ASCII);
string connection = reader.ReadLine;
reader.Close;
f.Close;
return connection;
}