ЧАСТЬ ПЕРВАЯ
"Коммунизм - есть ..." В.И.Ленин
Началось все с того, что кто-то когда-то придумал клиент серверную технологию, затем пришел SQL и большие деньги. Сейчас большинство российских программистов зарабатывают именно на этом. Хочешь не хочешь, но тебе приходиться этим заниматься. Темпы развития этой области, если это можно назвать развитием, сильно увеличивают роль "компьютерных данных" в деловой и политической жизни трудящихся. Дело даже не в законодательстве, а в том как поставлен "производственный процесс". Возьмем СБЕРБАНК. Там существуют две производственные линии: бумажная и компьютерная. Причем первая имеет определяющий приоритет. Компьютерная линия играет роль резервной или вспомогательной, т.е. для внутреннего использования. Если, например, главбух желает узнать сколько было перечислений на ТАКИЕ-ТО счета за ТАКОЙ-ТО промежуток времени, то ей не надо идти в архив или содержать персонал, чтобы выполнять такую работу по бумажкам. Она просто использует "компьютерные мощностя". Никакой другой роли компьютеры в СБЕРБАНКЕ не играют. Может быть с тех пор, как я не работаю на СБЕРБАНК, ситуация в чем-то изменилась. Но раньше было так, как я описал. Теперь возьмем ЧТО-ТО другое. Коммерческий банк или же просто фирму, занимающуюся оптовыми поставками ЧЕГО-ТО КУДА-ТО. Там несколько другая картина и некоторая зависимость от компьютера. Потому что не все можно хранить на бумаге. И не только поэтому. Иногда люди действительно пытаются улучшить свои дела путем компьютеризации производства. И это действительно происходит. Только в нашей стране большие деньги ассоциируются с воровством. Мы каждый день слышим эти возгласы о плохом начальстве, ужасном правительстве. Это очень больная тема - плохое начальство, ужасное правительство. Нам же интересна маленькая сторона этого вопроса. ТАКОЕ положение вещей оправдывает ВОРОВСТВО или любую другую неэтичность, как со стороны правительства, так и со стороны "трудящихся". И в результате мы получаем отсутствие правых и виноватых, потому что "по-другому в этой стране жить нельзя". Менталитет у нас видите ли такой. Ага, значит теперь это так называется. Будем знать. Итак, вы, как программист, может быть наняты либо одной, либо другой стороной. Я не думаю, что нет программеров, которые откажутся за крупную сумму денег написать бухгалтерию, которая позволяет "обманывать государство". Я на сто процентов уверен, что оооочень мало программистов, которых нельзя было бы заставить работать на спецслужбы. Попав на ту или другую сторону этой улицы, вы столкнетесь с проблемами безопасности. Хотя точнее было сформулировать проблему, как "УЯЗВИМОСТЬ КОМПЬЮТЕРНЫХ СИСТЕМ". Это видимость, а сущность - программер против программера. Только не как в фильмах, кровь здесь не льется, мир не спасется от БОЛЬШОГО УБЛЮДКА. Все гораздо прозаичней. Вы что-то строите, порой даже не представляя, что такое "ЛОМАТЬ". И некоторые ломают, так ничего серьезного и не создав. И вы знаете, по моему опыту, ломать гораздо проще, чем строить. И связано это вот с чем. Чаще процесс создания программы - это использования одного или нескольких API, соединенных неким алгоритмом. И, с одной стороны, программа должна быть переносима на большинство систем. А с другой стороны, доступ к ней должен быть строго ограничен. Противоречие между универсальностью и специализацией. И большинство программистов склоняются к универсальности. Они так привыкли делать. И никто даже не хочет задумываться над тем, как в целом будет выглядеть устойчивость системы. У универсальности есть одна противная черта. Это доступно и известно всем. Пример. Все вы знаете в чем отличие LINUX и BSD систем от WINDOWS. В создании последней участвуют от силы пара сотен человек. Во взломе WINDOWS участвуют тысячи. Гораздо больше сил бросается на штурм Юниксов. Но еще больше программистов-администраторов, используя принципы OpenSource, как можно быстрее устраняют обнаруженные дыры. И мы видим результат: Некоторые юникс-системы на порядок надежней и неприхотливей Windows NT и W2K вместе взятых. Мораль сей истории наводит на следующую мысль. Т.к. мы, программисты, вынуждены использовать универсальные API, то мы подвергаем тем самым свой продукт потенциальным атакам всех тех, кто знает этот API. Более того находятся те, кто знают API получше нашего и пишут такие полезные ИНСТРУМЕНТЫ ДЛЯ ВСЕХ, как SOFTICE. Поэтому получается, что чем универсальнее задача, тем она уязвимей, т.к. в ее ломке может участвовать любой хакер. Точно так же как вы полагаетесь на чей-то фирменный модуль защиты, точно так же не следует полагаться на невозможность анализа или закрытость или запутанность кода или структуры данных. Посмотрим как это можно применить на практике. Попробуем устранить уязвимость базы данных крупной фирмы. Причем уязвимость не только в плане получение любого доступа, но и в плане возможность оперативно продублировать систему, если старая была блокирована или "конфискована". Написал я как-то программу. Было это четыре года назад, поэтому писал я ее на Делфи с использованием BDE. По прошествии года эксплуатации я перенес базу этой программы с Interbase на Oracle. Теперь я прихожу туда только, чтобы заняться тюнингом сервера или переделкой отчетов. Времена больших добавлений прошли. Поэтому у меня была возможность пустить все свое время на распознавание образов. Недавно ситуация в стране изменилась - избрали нового президента. И вот однажды вечером, настроив параметры стоимостного оптимизатора, я был поставлен перед проблемой безопасности базы. Вопрос этот всплывал уже пару лет назад, но тогда угроза была призрачной и защита до реализации не дошла. Сейчас ситуация была в корне другой. Я не очень эрудированный человек, потому что люблю углубляться в изучение того с чем приходиться иметь дело. Поэтому говорю сразу - я не знаю как обстоят дела с этой проблемой на других SQL серверах. А проблема эта следующая:
- 1) Большинство баз данных, не важно клиент-сервер или dBase, не предусматривают в своей структуре данных администрирование прав. Данные хранятся в открытом виде, админ происходит на уровне программ, а не на уровне хранения.
- 2) Многие базы имеют СУПЕРПОЛЬЗОВАТЕЛЯ, пароль которого можно легко получить.
- 3) Многие базы рассчитывают на средства безопасности ОС.
Поясняю на примере ОРАКЛА. Оракл - это очень сложная и продуманная система, но для простоты понимания его условно можно разделить на две части:
- 1) Программная часть. Эта часть отвечает за запуск необходимых для работы сервера процессов. Последние в свою очередь играют роль транслятора запроса пользователя в ответ для него. Именно на этом этапе и только внутри этих процессов, происходят все события, которые мы называем "БЕЗОПАСНОСТЬ".
- 2) Информационная часть. Это банальные файлы данных, которые организуют пространство для работы ПЕРВОЙ части, т.е. для работы удаленных пользователей.
Ну что же все вроде правильно. Все да не все. АДМИНИСТРИРОВАНИЕ ПРАВ. Как оно здесь реализовано? В виде пассивного файла с пользователями, и в виде активного процесса, отвечающего за ЛОГИНы и за доступ до объектов. Поэтому по условиям нашей задачи, мы не можем рассчитывать на встроенную систему администрирования прав. Это не антиреклама ОРАКЛа, так обстоит дело у многих. ПОТОМУ ЧТО эти системы создавались для работы с УДАЛЕННЫМИ пользователями. Т.е. говоря по-простому, человек который все что угодно может делать на сервере, на котором установлен ОРАКЛ - НЕ ЯВЛЯЕТСЯ РЯДОВЫМ ПОЛЬЗОВАТЕЛЕМ. Оракл как и другие системы, мало что знают о безопасности ОС. ИМ этого и не надо. Они просто программы, которые администрирует СЕТЕВОЙ администратор. Оракл поставленный на сервер с НТ в этом плане мало чем отличается от почтового сервера установленного на тот же сервер. Я могу подойти к серверу, вставить в дисковод загрузочную дискету с ntfs-dos, нажать Reset, зайти в BIOS, набрав один из суперпаролей (AWARD_SW), поставить загрузку с флопа. После чего: гружусь, пускаю ntfs-dos, и я все прекрасно вижу. Все диски, файлы того же почтовика, или же ФАЙЛЫ ДАННЫХ ОРАКЛА. И я могу делать с ними что угодно. Два других способа. Первый: ставлю на ту же машину новую НТ и после установки все файлы доступны. Второй: вынимаю винт, подключаю к своей НТ и опять вижу всех и вся. Это самые распространенные и самые верные способы ВОЙТИ В ОТКРЫТУЮ ДВЕРЬ. В юниксах немного посложней. Там нельзя вынуть винт с одной машины и подключить к другой без логической реструктуризации корневой директории "приемной" машины. Но и тут есть обходные пути. В W2K все почти так же как и в НТ. Но там есть такая фича как EFS, Encrypt File System. Вы можете зашифрить файл или директорию. Шифровка и дешифровка происходит на лету. Алгоритм - модификация тройного DES. Но погодите ставить это чудо. Во-первых: вы можете легко с AstalaVista скачать эксплоит для этой "новинки". Эксплоит работает так: он берет "секретные" данные из временного каталога винды. Их же туда кладет EFS при работе "на лету" с зашифрованным файлом. Повторяется история с ДОСовскими секретными дисками. EFS проста - эксплоит еще проще. Ну ладно, допустим эксплоит не работает как написано в его readme, или же MS выпустит через пару недель фикс, который этот баг закроет. Что тогда? Опять скажу я вам - НЕ СПЕШИТЕ ставить W2K. Запомните на будущее: У продуктов MS плохая репутация обеспечения безопасности. И хакеры это неоднократно доказывали. Итак, EFS fixed! Что дальше? Дело в том, что EFS это надстройка над НТ. Т.е. просто прога работающая на уровне драйверов. Более того она использует симметричный шифр. Из этих двух данных следует, что НЕЛЬЗЯ СДЕЛАТЬ ЗАШИФРЕННЫМИ системные файлы или загрузочный диск. Что для работы, надо открыть проводник, т.е. залогиниться, и набрать пароль открывающий доступ к файлу. Оракл же, да и наверное многие подобные системы, ПОСТОЯННО ДЕРЖАТ файлы данных открытыми, в смысле file open (fopen, FileCreate, FileOpen). Понятно? Заходят к вам "маски-шоу", а сервер-то у вас включен и доступ к секретным файлам открыт. И что же? Вы побежите выключать сервер? Или проведете рубильник себе под стол? Ну что же в данной ситуации это единственный выход. Но есть решения и получше. Для тех кто забыл или просто запутался, сформулирую еще раз проблему. Как сделать невозможным доступ к информации в файлах данных, которые используют SQL-сервера? Вообще, когда задаются такие вопросы, то представляется слово ШИФР, написанное большими красными буквами. Но рассмотрим все подходы по порядку.
Предлагаемые решения.
Решение первое: У некоторых может возникнуть следующая мысль: "Там, наверху, сидят не очень умные люди". Да, я имел доказательства этому. Но полагаться только на это, значит не замечать очевидных вещей. Всегда исходите из данного "Программер против программера", а не "Программер против чайника".
Решение второе: Зашифрованный диск, или скажем больше СЕКРЕТНЫЙ диск. Но во-первых, я не нашел кряка из всех известным надежных дисков только для PGP-диска. К остальным системам кряки и эксплоиты появляются регулярно. Даже для альфа и бета версий. Во-вторых: допустим у вас есть диск, который не ломается (ха-ха). Что тогда? У всех дисков отсутствует фича "Дистанционное отключение". Поэтому такие диски полезны только если сервер ВЫКЛЮЧАЕТСЯ! Если кто-то уповает на то, что маски-шоу сами при своем визите вырубят электричество или дадут это сделать вам - то смотри РЕШЕНИЕ ПЕРВОЕ.
Решение третье: Шифрование полей таблиц. Ну-ну! Не вы первые, не вы последние. У Оракла не помню с какой версии поддерживается эта фича. Более того можно даже вставить е-key и думать, что вы в безопасности. История с e-key такая же как и с выключением электричества из ВТОРОГО РЕШЕНИЯ. При неожиданном визите такие вещи изымаются сразу. Если же изъятие сделать невозможно, то e-key можно проэмулировать. Итак, повторюсь, недостатки РЕШЕНИЯ ТРЕТЬЕГО "на лицо". Во-первых: у вас есть десятки тысяч строк кода, которые используют одну из самых непрозрачных библиотек доступа к данным - BDE. Чтобы вставить эту фичу вам придется перелопачивать всю вашу программу. На это уйдет масса времени, а маски-шоу не ждут, они рвутся в бой. Во-вторых: допустим, вы решились на подвиг, или ваша прога мала, или же вы тот счастливчик, кто писал проект на С++ с использованием родных прозрачных библиотек. Что же тогда? А вот что. Не вежливо, конечно, но встречный вопрос: У вас есть список серийных номеров товара. Таблица эта имеет рядовую длину 200тысяч строк(1год работы), пользователь при открытии накладной на 1000 единиц товара хочет видеть упорядоченный список серийных номеров, которые ушли по этой накладной. Простой рядовой запрос. Только поле серийных номеров шифруется, поэтому использование индексов невозможно, поэтому вам придется качать все 1000 строк и упорядочивать у себя. Отсюда большие требование к ресурсам и значительное замедление работы. Другой запрос: просто просмотр ВСЕГО списка товаров, упорядоченных по имени. И, наконец, убийственный запрос: ЕСТЬ ли серийный номер в базе и какова его история - накладные, расходники, гарантии, кто собирал, откуда пришло и т.д. ВАМ придется делать FULL RANGE SCAN по всем 200 тысячам строк таблицы серийников, дешифровать каждый и сверять с эталоном. УАУ! Тут-то придет каюк любой системе. Такой мааааленький каючек. Обходные решения этой проблемы влекут за собой недостатки РЕШЕНИЯ ВТОРОГО и ПЕРВОГО. (Обходные решения: дешифрация на сервере или открытые данные и шифрация трафика, см Oracle Application Server). Ну вот и все. На этом можно было бы и закончить. Потому что четко сформулированный вопрос содержит в себе ответ. Мы рассмотрели все основные подходы при решении поставленной задачи. И после их рассмотрения вырисовываются четкие контуры системы безопасности:
- 1) Не требует изменения ни клиентской ни серверной части
- 2) Не сказывается на быстродействии
- 3) Не мешает старой системе безопасности на уровне SQL запросов
При этом выполняет следующие функции:
- 1) Возможность моментального, доли секунды, дистанционного закрытия ВСЕХ данных
- 2) Возможность перевода ВСЕЙ системы на время(на ночь или выходные) в полностью закрытое состояние.
Все! ...
Я представлю на ваш суд одно из самых простых решений - РЕАЛЬНУЮ ЖИВУЮ версию этой системы, которая может быть поставлена на любую ВАШУ систему. Если кого это интересует, читайте дальше. Итак, реализация. Для большей гибкости я постараюсь не просто привести скрипты, но и описать что они делают, т.к. возможно вам придется их менять или писать свои же. Система разбита на две части, которые каждая в отдельности отвечает за одну из двух названных мной возможностей. Так же я сразу вам укажу недостатки этой системы, чтобы вы могли дальше развивать ее или добавить что-то свое, чтобы сделать систему более надежной. Начнем с первой возможности. Чтобы ее описать хочу вам напомнить, что в Оракле помимо SQL существуют еще два языка: PL-SQL и DDL. PL-SQL язык программирования запросов используется ТОЛЬКО для написания хранимых процедур. DDL - это Data Description Language, язык описания данных, встречается также название DML - язык манипулирования данными. Но это уже не столь важно и принципиально. Т.к. я сейчас уже не использую BDE и Делфи, то не знаю как там обстоят дела с поддержкой DDL. Но раньше, эта поддержка была слабой. Вам же нужно иметь библиотеку классов, ну или компонент если хотите, которые позволяют вам писать в клиентской проге такие команды DDL как drop table, drop user, truncate table, drop tablespace и т.д. Далее получив такие компоненты для своей проги, вставьте простой скрипт drop user cascade повесьте это на любую горячую клавишу. Важно чтобы она была проста для юзера, например Ctrl-F12. Здесь уже вам нужно решить самостоятельно. Главное, чтобы нажатие на эту клаву грохало схему с рабочими таблицами, если у вас таблицы расположены в нескольких схемах воспользуйтесь удалением tablespace, т.е. drop tablespace, если в нескольких tablespace-ах, то грохните всю базу, т.е drop database. Теперь посмотрим как выполняются следующие требования к системе:
- 1) Возможность моментального, ДОЛИ СЕКУНДЫ, дистанционного закрытия ВСЕХ данных
- 2) Не мешает старой системе безопасности на уровне SQL запросов
Начнем с конца. Так проще. Вам нужно сразу сообщить о такой возможности заказчику и сразу же популярно объяснить почему вы это делаете. Может быть он пожелает сделать эту горячую кнопку только для себя и своего заместителя? Обрисуйте ему ситуацию целиком, не держите людей в темноте. Это плохо сказывается на их психике и вашей зарплате. Сразу скажите ему, что при введении такой kill-клавиши надо решить проблему с обеденным перерывом, не закрывать же базу на 40 минут? Кто-то должен потерпеть, либо раньше пообедать, либо обедать на рабочем месте. Не пускайте это на самотек. Пусть кто-то за это отвечает, потому что там где нет ответственности - разруха и бардак, потому что это ничье. Больше ответственности - больше контроля. Да и еще не усложняйте систему, чем сложнее система, тем больше шансов, что она либо не сработает, либо сработает не вовремя. Под эти слова также попадает ваше желание поставить на эту kill-клавишу пароль. Не делайте этого. Маски-шоу не дадут набрать пароль. Далее о моментальности. В Оракле файлы данных организованы, как маленькая файловая система внутри ОС. Роль секторов выполняют блоки, размер блока соответственно определяет сколько строк таблицы будет прочитано за одну ФИЗИЧЕСКУЮ операцию чтения с диска. Но не надо увлекаться у этого есть и отрицательные стороны. Нужно четко знать сколько занимает строка таблицы при вставке и при максимальном заполнении, сколько одновременных транзакций бывает на этой таблице, но ответы на все эти вопросы вы должны знать при построении базы или при ее тюнинге. Это интересная тема, она мне очень нравится, но это не повод отвлекаться от основной темы статьи. Вернемся к блокам. Система работала бы очень медленно, если бы она имела только этот уровень абстракции между физическими данными и логической структурой информации. Поэтому есть еще одно понятие EXTEND, не будем его переводить просто транслитерируем. Значит, Экстент - это группа рядом расположенным блоков, которые принадлежат той или иной логической структуре, например, таблице или индексу или сегменту отката или временным данным по выполнении сортировки или представлению или подзапросу. Вот это понятие вам нужно хорошо усвоить. Экстент, его определение и развитие сильно влияет на быстродействие и устойчивость базы данных. Как должен вести себя в будущем при работе с данными Экстент - это его основная задача и роль в работе сервера. Экстент для таблиц это одно, для сегментов отката это другое. Сейчас, не углубляясь в тюнинг базы, вам нужно знать, что стартовый Экстент таблицы должен помещать в себя все ее строки, т.е. их объемы должны быть равны. Точно также тщательно вы должны спланировать это для индексов. Это занимает пару часов. Итак если у вас таблицы расположены в одном двух Экстентах, то удаление таблицы drop table, а не delete, будет происходить моментально. Потому что Экстент метиться как свободный, а с ним и блоки. В моем случае на слабеньком сервере(Celeron 333 разогнанный до 415 плюс 20Г Seagate UW-SCSI) удаление базы с общим количество строк около миллиона заняло 1-2 секунды, может меньше, у меня часы, а не секундомер. Короче это было очень быстро. Или нет, скажем так это было достаточно быстро. Но здесь вырисовывается еще один недостаток системы. Данных в базе нет, вернее базы нет, но следы данных остались. Любое решение реализующее чистку данных требует нескольких минут работы. Это не подходит. Я решил попробовать сам восстановить данные, которых нет в системе, по оставшимся следам. Сначала я полез в Инет и стал искать утилитки для таких целей. Было потрачено несколько часов, но ничего нужного для таких задач я не нашел. Я попробовал найти документации по внутренней структуре данных в оракле, частично оная была обнаружена. Это была документация самого оракла по структуре блока, но не больше. Инфы по организации Экстентов и привязки их к таблицам и индексам я не нашел. В конце концов мое желание оставить все как есть победило и я остановил поиски. Тут еще один усложняющий момент. Форматы файлов данных меняются от версии к версии. Иногда это значительные изменения, иногда нет. Но все эти трудности не означают, что надо вообще не исследовать эту проблему. Но пока единственным решением этого недостатка нашей системы безопасности является создание "гнилой" таблицы сразу после удаления рабочих таблиц. Гнилая таблица должна иметь стартовый экстент размером с общий объем удаленных таблиц, и в нее нужно назначить запись мусора. DDL не задействует механизм транзакций, поэтому команда create table my.musor select * from sys.all_object будет выполнятся пока не вырубят электричество, а когда потом включат уже будет таблица с некоторым количеством строк. Примечание счастливым обладателям Oracle8, в оном есть триггера на логин, в которые можно вставить всякую чушь, вроде гроханья таблиц или создания мусорной таблички. Дальше все в вашей власти. Закончим пожалуй с клиентской частью, а точнее с kill-клавишей и МОМЕНТАЛЬНЫМ удалением таблиц. Займемся другой возможностью. Закрытие базы на ночь или на выходные. Есть несколько подходов к этому вопросу:
- 1) Секретный диск. Ну что же раз мы функцию дистанционного отключения уже реализовали, то мысль о шифрации закономерна. Преимущества: простота использования - просто выключаешь сервер уходя с работы. Недостатки: неизвестно есть ли кряк для выбранного вами диска плюс проигрыш в скорости.
- 2) Вынос базы: здесь все еще проще. Винт с базой ставиться во внешний SCSI разъем или в гнездо RAIDа или FireWire. По завершении работы винт выноситься и запирается в сейфе, сейф либо дома либо в банке, либо еще где. Преимущества: простота использование плюс вы носите с собой все данные. Недостатки: вы носите с собой все данные плюс большая цена.
- 3) Удаление всей базы и восстановление ее из запароленных архивов. Преимущества: просто и дешево. Недостатки: сильная фрагментация дисков, долгая процедура включения, наличие кряков паролей для всех архивов.
- 4) Шифрация базы: см. пункт 1). плюс, к старым недостаткам, - фрагментация дисков
- 5) Частичное удаление ключевых данных. Например, удаление справочника товаров и клиентов. После чего база становиться не функциональной.
Вы можете выбрать любой из понравившихся вам способов или придумать свой. Я не обладал запасом времени и возможностями апгрейда техники, поэтому выбрал свой способ - некая помесь 4) и 5). Относительно пункта 5 все понятно. В Оракле есть замечательная команда TRUNCATE TABLE, которая удаляет все строки таблицы не задействуя механизм транзакций. Т.е. описание таблицы остается, а ее Экстенты метятся как свободные. Короче работает это дело весьма быстро. Единственное, что нужно сделать - это выключить все FOREIGN констрейнты, т.к. удалять вы будете в основном справочники, потому что в большинстве случаев только они содержат символьные поля. Конечно и в других таблицах они могут быть, но они не играют столь решающей роли, как символьные поля в справочниках. Получить этот скрипт просто: select 'alter table '||ac.owner||'.'||ac.table_name|| ' disable constraint '||ac.constraint_name||';' from sys.all_constraints ac where ac.owner = 'MY_SHEMA' and ac.constraint_type='R' сохраните результаты через spool или через clipboard и вы получите: connect internal/superpsw alter table MY_SHEMA.PHONE_BACK disable constraint BACK_CLIENT; alter table MY_SHEMA.PHONE_BACK disable constraint BACK_MODEL; alter table MY_SHEMA.PHONE_IN disable constraint IN_MODEL; alter table MY_SHEMA.PHONE_IN disable constraint IN_SELL; alter table MY_SHEMA.PHONE_OUT disable constraint OUT_CLIENT; alter table MY_SHEMA.PHONE_OUT disable constraint OUT_MODEL; alter table MY_SHEMA.PHONE_OUT disable constraint OUT_SHOP; alter table MY_SHEMA.REPAIR_INFO disable constraint RINFO_SN; alter table MY_SHEMA.REPAIR_INFO disable constraint RINFO_WORKS; alter table MY_SHEMA.SPRSN disable constraint SPRSN_WORK; exit все можете передавать это ServerManager-у. После пустить скрипт с truncate table MY_SHEMA.sprmodel truncate table MY_SHEMA.sprclient Теперь эти две таблицы пусты и база не функциональна. Чтобы не случилось страшного и можно было бы включать таблицу, вам надо вызвать ЭКСПОРТ ВСЕЙ базы и этих двух таблиц в отдельности. Всю базу вы экспортируете на случай, если после визита МАСКИ-шоу вам придется все вертать на место. Так же имея копию базы, можно ее развернуть в любом месте за считанные часы.
... >> Продолжение ...
Содержание Назад Вперед