четверг, 16 октября 2014 г.

Коротко. Баг в BCP.

Вкратце, утилита BCP версии 11.0.2100.60, устанавливаемая вместе с SQL Server 2012, при экспорте из базы данных в файл, и использовании форматного файла, выполняет запрос к данным ДВАЖДЫ, что может привести к неожиданным результатам, если запрос производит сторонние действия. Подробности и способ воспроизведения бага в моей теме на форуме.

четверг, 25 сентября 2014 г.

Брутфорсы и SQL

Предыстория.

Так случилось, что бизнесу потребовалось вывести базу  данных со своей территории. Спрятать её, исключить  физический и информационный доступ всем потенциально заинтересованным лицам, и при этом сохранить возможность  полноценно, (и совершенно секретно), работать с ней из  центрального офиса.
Требование есть требование. Раз оно поставлено, нужно  его выполнить.
Оборотистые наши админы нашли и арендовали забугорный хостинг с SQL Server, настроили подключение к нему  через интернет, развернули свежую архивную копию, и...  отдали базу в распоряжение программиста, (пишущего эти строки).
Нужно заметить, что и сама база, и самописная клиентская  часть, (на Delphi), за свою толи 15-ти, толи 20-летнюю историю пережила несколько поколений поддерживающих и развивающих её программистов, разного уровня квалификации, и различных взглядов на философию программирования. Стоит ли удивляться, что немалая часть функциональности программы, написанной в предположении, что база данных всегда располагается в локальной сети, после переезда за границу конторы стала тормозить, и местами просто безбожно. Загрузка двухсотстрочного документа в таблицу, перемежаемая уточняющими запросами к базе по каждой строке, из 15-секундной операции превратилась в 2-минутную. Впрочем, мой рассказ сегодня не об этом, (как следует из заголовка).
Казалось бы, задача, поставленная руководством, была решена. База "жила", пользователи "пользовались". Однако, выполнение требования полностью спрятать информацию и факт её существования от чужих глаз, вдруг оказалось под угрозой...


Появление проблемы.

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


 Небольшое расследование прояснило, что IP-адреса, упоминаемые в поле Message, не принадлежали нашему офису, и были разбросаны по всему миру. Большая часть находилась в Азии, некоторые в Канаде, России, США... География и периодичность запросов навели на страшную догадку: мы стали жертвой хакерских атак! К нашему SQL Server-у пытаются подобрать пароль! И мы, как оказалось, не могли этому воспрепятствовать. Всезнающие наши админы заявили, что, в принципе, запретить все IP-адреса, кроме нескольких "легальных", возможно, при этом, не перекрывая обычный HTTP-траффик, (он был нужен для работы), но, как это реализовать практически, "они сейчас не знают". Так и возникла передо мной задача, как сервер от брутфорсов защитить.

Решение - часть 1.

Я не админ, обычный программист. Правильные методы решения админских задач мне неизвестны, и потому описывать я буду то решение, которое придумал сам. Всё, что мне пришло тогда в голову, можно вкратце описать алгоритмом:
1. Каким-то образом своевременно получать уведомления о начале хакерской атаки, с указанием вредоносного IP-адреса.
2. И совершенно неизвестным пока способом блокировать этот адрес на сервере, где установлен SQL.
С решения первого вопроса и начнём. Единственным доступным мне источником входящей информации, (о неудачных логонах), оказался сам журнал ошибок SQL Server. Достаточно было его просто прочитать, и выделить нужные строки. Мне повезло: в арсенале предусмотрительной Microsoft нашлась недокументированная процедура xp_readerrorlog для чтения журнала, а в интернетах - достаточно полное описание её синтаксиса и параметров. Процедура возвращает содержимое журнала в виде набора данных из трёх полей, отфильтрованного по дате-времени и тексту ошибки. Так удалось воспроизвести историю неудачных логонов, с точностью до десятых долей секунды.

exec xp_readerrorlog 0, 1, 'Login failed for user', 'provided'

 После некоторых преобразований, нетрудно получить табличку вида [Время], [IP].


Всё, что осталось сделать - решить, какие IP-адреса из этого списка являются "зловредными". Идею, на качественном уровне, можно выразить так: нежелательным считается такой IP-адрес, который:  а) довольно часто, и б) в больших количествах появляется в указанном списке. Было придумано несколько эвристических правил, опирающихся как на разницу во времени между "соседними" событиями с одним и тем же IP-адресом, так и на количество таких событий в определённые интервалы времени. В процессе экспериментов были подобраны числовые параметры для упомянутой эвристики, и реализован SQL-запрос, возвращающий хакерские IP-адреса. Создав несложный джоб в службе SQL Server Agent, раз в несколько минут проверявший журнал  ошибок, я стал регулярно получать на почту списки хакерских IP-адресов. Таким образом, первую часть задачи можно было считать решённой.

Решение - часть 2.

А вот со второй частью задачи, ограничением сетевого трафика по списку IP-адресов, пришлось повозиться. Помимо того, что она относилась к малоизвестной мне области - администрированию Windows, оказалось, что почти нигде в интернетах не присутствует описания программных способов это реализовать. Большинство "руководств" рассказывало, какие кнопки в каком файрволе нужно жать, чтобы получить требуемый эффект.
Для начала, удалось обнаружить, что в составе ОС Windows есть инструментарий "Политики IP-безопасности", имеющий графический интерфейс, и позволяющий блокировать сетевой трафик для списков IP-адресов, в чём я не преминул убедиться собственноручно.
 

Убедившись в этом, я стал искать способ делать то же самое программно, без непосредственного участия пользователя. Как я рассуждал: "Если есть одна программа, которая что-то делает, значит можно написать другую программу, которая будет делать тоже самое, используя те же внутренние механизмы, что и первая. И, вероятнее всего, эти механизмы присутствуют в WinAPI".  
Как же я ошибся! Такого WinAPI нет. Вообще. Совсем. Осторожная Microsoft не создала такого API, (либо не опубликовала).
И всё, что у нас есть - это утилита командной строки netsh.exe, работающая в контексте ipsec, (Прим.: в новых версиях Windows рекомендуется использовать контекст advfirewall). Достаточно программно создать этот процесс с нужными параметрами в строке запуска, и он внесёт необходимые изменения в политику безопасности сервера. Что я и реализовал в программе-роботе, периодически проверяющем журнал ошибок SQL Server на предмет появления хакерских атак, и в случае нахождения таковых, добавляющем вредные IP-адреса к блокирующему фильтру политики безопасности сервера.

вторник, 23 сентября 2014 г.

"В потоке" (лирично)

Во взгляде - смысл, моноширинно-чистый,
Наушники - глотатели шумов.
Не отвлекайте тленом программиста,
В плену абстракций, в символизме слов.

"Стратегия", "Команда", "Одиночка", -

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

Неоднозначно. Имя переменной - 

Метафора, значение и суть.
К двоично-однобитовой вселенной
Как третье состояние примкнуть?

Begin и end - четверостиший скобки,

Ступени отступов, перила try-except,
Константы, расположенные в стопку,
Использует дженериков адепт.

Спешат минуты, незаметно-быстро,

Сверкает мысль в императиве строк.
Не отвлекайте чушью программиста,
Не прерывайте творческий поток.