Публикация научных статей.
Вход на сайт
E-mail:
Пароль:
Запомнить
Регистрация/
Забыли пароль?
https://wos-scopus.com
Научные направления
Поделиться:
Статья опубликована в №34 (июнь) 2016
Разделы: Информационные технологии
Размещена 24.06.2016. Последняя правка: 05.01.2017.

Общие рекомендации для проектирования и реализации WEB-сайтов

Гутовский Дмитрий Игоревич

Государственное бюджетное образовательное учреждение высшего образования Московской области Университет «Дубна»

студент-магистрант

Филозова Ирина Анатольевна, старший преподаватель кафедры распределённых информационно-вычислительных систем Института системного анализа и управления государственного университета "Дубна"


Аннотация:
Тема актуальна, так как при выполнении сложных проектов, связанных с разработкой сайтов, возникает множество проблем, автоматическое решение которых – невозможно.


Abstract:
The topic is relevant, as in the performance of complex projects related to developing websites, there are a lot of problems, automatic solution of which is impossible.


Ключевые слова:
сайт; базы данных; WEB-разработка.

Keywords:
website; databases; WEB development.


УДК 004.7

Введение

В настоящее время существует множество различных технологий разработки и управления WEB-сайтами и приложениями с клиент-серверной архитектурой. Это многообразие технологий предоставляет выбор разработчикам, для обеспечения наилучшей эффективности, надёжности и безопасности, однако имеется множество трудностей с выбором тех или иных средств, для выполнения конкретной задачи.

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

В большинстве современных проектов, разработчики используют и ручное написание кода, и готовые модули. Также разработчики часто обращаются к различным системам, на базе которых ведутся разработки WEB-приложений. Положительными аспектами такого подхода являются: 1) облегчение разработки собственных модулей; 2) возможность эффективного повторного использования кода. Это существенно снижает сложность при разработке системы в целом.

Несмотря на то, что при использовании наработок человечества, задача разработчика в некоторых ситуациях облегчается, это может давать и множество «подводных камней», поскольку не весь код зависит от конкретной команды разработчиков, да и при работе над сложными проектами возникают новые ошибки. Несмотря на то, что при грамотном использовании опыта сообщества web-программистов, перенимаются и ошибки, отказаться от готовых модулей, а также систем разработки и управления контентом - практически невозможно, ввиду большой сложности и высокого объёма работ при создании крупных проектов. Встаёт вопрос о достоинствах и недостатках различных средств и технологий.

Цель

Предоставление дополнительных возможностей WEB-разработчикам для эффективной реализации приложений различной сложности.

Анализ предметной области

Описание общих принципов работы сайта

В общем случае сайт состоит из двух основных частей – клиентской и серверной.

На стороне клиента работает браузер, который интерпретирует связку из языков HTML, CSS и JavaScript.    

HTML(Hyper Text Markup Language) содержит разметку WEB-страницы.

CSS(Cascade Style Sheet) содержит стили оформления различных элементов разметки WEB-страницы.

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

На стороне сервера, в общем случае, имеется программа-WEB-сервер, содержащая интерпретатор какого-либо серверного языка (например, PHP, Python, Ruby и т.д.) и программа, выполняющая роль сервера баз данных (как правило – содержит интерпретатор какого-либо диалекта языка SQL, но могут быть и другие языки для работы с базами данных).

В общем случае, сайт работает следующим образом:

  • Клиент пересылает на сервер запрос;
  • Запрос интерпретируется WEB-сервером;
  • Вызывается нужное приложение, которое обработает запрос пользователя при помощи интерпретатора серверного языка программирования;
  • Строятся запросы к базе данных (БД) и передаются соответствующему серверу;
  • Сервер баз данных выдаёт нужную информацию из БД;
  • Интерпретатор серверного языка программирования, в соответствии с кодом приложения и информацией из БД, формирует ответ для клиента, состоящий из HTML, CSS и JavaScript.

Как правило, web-приложение содержит как код серверной части, так и клиентскую часть — HTML, CSS и JavaScript код. Серверная часть кода не передаётся клиенту и ни как им не интерпретируется. В общем случае, частично клиентская часть приложения генерируется динамически серверными скриптами.

Общее описание назначения CMS

CMS(Content Management System) или – система управления содержимым (контентом) представляет из себя информационную систему с WEB-архитектурой, в которой реализованы функции:

  • Создания содержимого;
  • Редактирования и контроля содержимого;
  • Представления содержимого в виде, удовлетворяющем правилам, прописанным в активных шаблонах;

По своей сути, CMS является сайтом, состоящим из двух основных частей – бэкэнда и фронтэнда.

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

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

Для примера рассмотрим форум, построенный на базе форум-ориентированной CMS phpBB. В данной ситуации, бэкэндом является часть, в которой пользователи, администраторы и модераторы создают и редактируют темы, разделы, объявления и т.д. В зависимости от роли конкретного пользователя (обычный пользователь, администратор или модератор), ему доступны разные права. Фронтэндом же является часть, которую видит и может использовать на сайте данного форума любой незарегистрированный пользователь.

Сравнительный анализ CMS систем

Для сравнения были выбраны наиболее популярные, но существенно отличающиеся CMS. Сначала был проведён анализ двух наиболее популярных PHP CMSWordPress и Joomla, после чего они были сравнены отдельно с CMS MODx, так как её концепция кардинально отличается от концепции WordPress, Joomla и большинства других CMS.

При выборе CMS следует чётко понимать задачу, которую должен будет выполнять разрабатываемый WEB сайт. Существует давно сложившиеся мнение, что WordPress применяется преимущественно для сайтов со структурой блога, а Joomla – наиболее универсальное решение. Не так давно это было справедливо, однако сейчас – всё уже не так очевидно.  Рассмотрим поподробнее.

Во-первых, WordPress имеет наиболее простой и интуитивно понятный интерфейс, который позволяет вносить ручные изменения кода, практически в любом месте шаблона, в следствие этого, для разработчиков не составит большого труда создать структуру, отличную от блога. Конечно же и Joomla имеет довольно большой и вариабельный функционал для работы с различными частями шаблона, позволяя размещать их на разные позиции и редактировать. Однако, гораздо сложнее добавлять, удалять и изменять код этих модулей. Например, в Joomla нет возможности просто прописать “служебные тэги” прямо через редактор исходного кода (style, link и т.д.), а также скриптовые (Script). Это усложняет возможность подключения скрипта или внешней таблицы стилей к конкретной странице. Также отсутствует возможность прямого получения постоянных ссылок на материал. Все эти нюансы усложняют действия в нестандартных ситуациях, которые нельзя решить по средствам плагинов или встроенного функционала. Поэтому, несмотря на более сложный и функциональный интерфейс Joomla, ручное кодирование в ней сложнее, чем в WordPress. Это усложняет жизнь разработчикам, однако в стандартных случаях, с которыми чаще сталкиваются начинающие, это может помочь добиться более эффективного результата, с наименьшими усилиями. В целом - Joomla имеет менее понятный интерфейс.

Преимущества и недостатки Joomla

Одним из наиболее весомых преимуществ Joomla является универсальность. Её можно использовать для создания сайтов с различными структурами. Не менее важным преимуществом является и большая функциональность, в том числе и гибкая настройка прав пользователей. Третьим, и последним явным преимуществом является визуальная модульная настройка страниц, а, именно, размещение модулей на различных позициях (местах) страницы и независимая настройка каждого модуля.

 

Рис. 1. Отображение позиций в Joomla

Одним из наиболее важных недостатков Joomla является несовместимость версий. А именно, если Вы берёте шаблон для Joomla 2.5, то корректно установить его на Joomla других версий будет очень сложно, или практически невозможно (потребуются множественные изменения кода — фактически создание шаблона заново). Это бывает очень часто, и не только для шаблонов, но и для других компонентов (например, плагинов).

Причём нет совместимости не только с более новыми версиями (что весьма понятно), но и обратной совместимости. Второй недостаток – сложность ручной коррекции кода (например, подключения стилей или скриптов). Конечно, есть множество плагинов и шаблонов с нужными эффектами (в том числе и стилевыми), однако в реальных проектах, довольно часто требуется разработка и внедрение собственных частей, при этом не настолько сложных, чтобы писать отдельные модули по всем правилам Joomla. Ещё один недостаток, который очень часто проявляется – индивидуальная регистрация устанавливаемых компонентов. А именно, нельзя просто скопировать содержимое плагина или шаблона в соответствующую папку структуры сайта. Это бывает практически всегда. Чтобы корректно установить тот или иной компонент очень часто требуется произвести их добавление через панель управления, а это – дополнительная настройка прав пользователей уже непосредственно на уровне хоста. Ещё один недостаток — радикальная смена интерфейса у различных версий Joomla. Этот недостаток наиболее важен не для разработчиков сайта, а для людей, которым предстоит с ним работать в дальнейшем. Так как пользователи сайта могут быть далеки от WEB-разработки, то их обучение и привыкание к новому интерфейсу может быть весьма сложным. Конечно, есть темы панели управления, но подходящей может и не быть, а разработка собственной сильно усложняет проект.
 

Рис. 2 Панель управления Joomla 2.5

 

Рис. 3 Панель управления Joomla 3

 

Преимущества и недостатки WordPress

В отличии от Joomla, в WordPress доступна практически 100%-я возможность ручного кодирования (как страниц, так и модулей). Это преимущество сильно выручает тогда, когда нужна очень тонкая настройка, которой нет в средствах самой системы (или шаблона), а также, когда требуется добавление сторонних компонентов, не представленных в виде плагинов или других модулей. Например, можно без труда подключить свою таблицу стилей на конкретную страницу (или свой скрипт), не регистрируя их в самой CMS. Это сильно облегчает задачу работы с нестандартными, но мелкими частями, которых нет в установленных темах (или плагинах), но они малы (или редко используются), чтобы регистрировать их, или писать отдельную тему (или плагин). Второе важное преимущество – это обратная совместимость версий. То есть, если вы используете шаблон (или другой компонент) для конкретной версии WordPress, а потом обновляете свою CMS, то в большинстве случаев всё будет работать корректно. Также не вызовет проблем установка более старого шаблона и/или другого компонента на более новую версию WordPress. Ещё одним преимуществом является схожесть интерфейсов различных версий этой CMS, что упрощает жизнь модераторам сайта. Ниже приведены снимки экранов рабочих сред у разных версий WordPress.

 

Рис. 4 Панель управления WordPress 3.3

 

Рис. 5 Панель управления WordPress 4.2.2

Немаловажным положительным аспектом является и то, что большинство устанавливаемых компонентов не требуют индивидуальной регистрации. То есть для того, чтобы установить новый шаблон (или плагин), нужно просто скопировать папку, содержащую его, в соответствующую папку в структуре сайта. Это облегчает настройку прав доступа, непосредственно на уровне сервера, а также упрощает задачу переноса сайта и смены доменного имени. Интерфейс WordPress более логичен, чем интерфейс Joomla. Один из примеров — при создании новой страницы, вы получаете постоянную ссылку на неё, чего по умолчанию нет в Joomla.

Одним из наиболее важных недостатков данной системы является её направленность на блоговую структуру, однако этот недостаток легко исправляется добавлением и настройкой различных шаблонов, плагинов и ручной правкой кода. Второй недостаток – менее гибкая, по сравнению с Joomla, настройка прав доступа и ролей пользователей, однако и он легко исправляется, также как и в предыдущем случае. Третьим недостатком является отсутствие визуальной модульной настройки сайта (как в случае с позициями в Joomla), однако этот недостаток весьма спорный, так как при большом количестве модулей, довольно часто, легче использовать ручное изменение кода.

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

Исходя из всего вышесказанного, можно сделать вывод, что применение Joomla достаточно трудоемко как при разработке, так и при дальнейшем использовании сайта. Поэтому, если при разработке конкретного web-приложения нет каких-либо специфических задач, легко реализуемых на Joomla, то стоит обратить внимание на WordPress.

Преимущества и недостатки MODx

Одной из наиболее удобных универсальных CMS является MODx. В отличии от большинства других наиболее популярных CMS, например, таких как WordPress или JoomlaMODx имеет существенное отличие, которое является преимуществом в сложных и нестандартных проектах.

При использовании MODx можно произвести установку абсолютно любого шаблона. Для MODx не нужно специально адаптированных тем, шаблон может быть написан вручную средствами HTMLCSS и JavaScript. Так же шаблон может быть получен из любого сайта или шаблонов для других CMS.

Шаблоны для большинства CMS написаны по их правилам, вызывая различные функции CMS и используя её библиотеки. Такой подход даёт возможность установки и настройки шаблона не по средствам написания кода, а через графический интерфейс самой CMS, однако этот способ накладывает ограничения на шаблон, ведь он должен быть написан по стандартам конкретной модели (а у некоторых CMS и конкретной версии). Можно провести аналогию с ОС. Для установки приложения, например, на ОС Windows, это приложение должно соответствовать определённой архитектуре и его установка на MacOS или Linux будет невозможна без специальных дополнений и/или эмуляторов ядра Windows.

Рассмотрим установку и настройку шаблона в MODx.   Допустим, был скачен понравившийся шаблон или написан самостоятельно. Пусть этот шаблон содержит HTML CSS и JavaScript. Для установки шаблона потребуется выполнить следующий набор действий:

1. Скопировать папку с шаблоном в произвольную папку, лучше, чтобы она находилась внутри папки CMS, как и другие элементы структуры сайта (обычно, шаблоны в MODx хранят в папке assets/[имя папки для шаблонов]/[имя папки с конкретным шаблоном]. Однако, можно хранить содержимое шаблона и в других папках.

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

4. Получить код шаблона из его главной HTML-страницы: копируем его и вставляем в соответствующее поле в MODx.

5. Сохранить шаблон.

6. Отредактировать пути подключаемых ресурсов (JavaScriptCSS и т.д). По умолчанию корневой папкой конкретного сайта считается папка с самим MODx, соответственно, если шаблон хранится, например, в папке “шаблон”, а папка “шаблон” хранится в папке “assets”, то путь к содержимому будет assets/шаблон/. Допустим, создаваемый шаблон, находящийся внутри папки “шаблон” имеет папки “CSS” и “JS”, внутри которых находятся стили и скрипты соответственно, так же имеется файл index с расширением HTML. Кодом этого шаблона будет содержимое индексного файла, а в путях подключения стилей напишем assets/шаблон/CSS/[название файла стилей].css. Аналогично редактируем пути к скриптам и другим ресурсам шаблона. После правильного редактирования путей, страница, использующая данный шаблон должна выглядеть как он сам, однако при её редактировании средствами MODx, не будет видно никаких изменений, они будут видны только при условии внесения их в код самого шаблона, ведь, по сути он и становится кодом страницы, которая его использует.

7. Для того, чтобы это исправить, потребуется адаптировать шаблон к работе в MODx. В системе MODx имеется такая сущность как “поля”. Каждое поле имеет своё название. Например, в MODx Revolution, поле для имени страницы (заголовка (не путать с понятием заголовка в HTML)) имеет название [[*pagetitle]]. Следовательно, для того, чтобы введённый в графу “заголовок” текст стал именем страницы, требуется удалить из тега его содержимое и вставить название данного поля. Это будет выглядеть так:

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

Это описана общая схема настройки шаблона, более тонкая настройка зависит от ситуации [5].

Теперь рассмотрим варианты с получением шаблона из другого сайта, или другой CMS. Допустим, была найдена понравившаяся страница какого-то сайта. Для того, чтобы получить из неё HTML шаблон, требуется скопировать её исходный код, а также коды и файлы ресурсов, которые она использует (нужно для дальнейшего их редактирования). Далее необходимо сохранить скопированные коды в соответствующие файлы и разместить в соответствующих папках. Полученный шаблон настраивается по классической схеме. Для получения шаблонов для работы с MODxиз других CMS требуется создать страницу с использованием понравившегося шаблона на CMS-доноре, а после выполнить те же действия, что и в предыдущей ситуации.

Также, MODx имеет понятный интерфейс, который не изменяется радикально при появлении новых версий.

 

Рис. 6 Вид панели управления MODx Revolution 2.2.7

Рис. 7 Вид панели управления MODx Revolution 2.4.2

Несмотря на то, что в MODx нет некоторого функционала, который практически обязателен (например – очень мало-функциональный редактор содержимого страницы), это легко устраняется, так как для MODx (как для Revolution так и для Evolution) написано множество плагинов, сильно расширяющих функционал.

Список плагинов, которые желательно установить сразу после установки MODx, независимо от направленности разрабатываемого сайта:

  • CKEditor – “продвинутый” редактор содержимого страницы. Добавляет множество функций, присущих популярным текстовым процессорам (таким как Microsoft Word или LibreOffice Writer). Этот плагин имеет множество альтернатив, и несмотря не его простоту, он также имеет определённые недостатки. Основной недостаток заключается в использовании нежелательных стилевых конструкций HTML (см. раздел “Задача унификации и оптимизации вёрстки”);

  • CodeMirror – удобный плагин для кодирования элементов шаблона. Подсвечивает незакрытые HTML-тэги, и некоторые другие ошибки кода;

  • SimpleSearch – простой плагин для реализации функции поиска по сайту (на фронтэнде);

  • Wayfinder – не менее простой и удобный плагин для реализации меню сайта;

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

Есть так же популярная CMS-конструктор WiX, которая использует противоположную MODx концепцию. Конечно для создания простых проектов WiX может показаться легче, так как в ней реализована 100% генерация кода, и пользователь работает только через средства графического интерфейса, однако отойти от возможностей, которые заложены разработчиком практически невозможно, что очень затрудняет разработку индивидуальных проектов на WiX. При этом разработка новых элементов, работающих на WiX будет весьма затратной, так как в коде своего компонента нужно будет реализовать множество конструкций, работающих по правилам функций WiX, для обеспечения полной функциональности, которая отображается в интерфейсе данной CMS.

CMS Joomla и WordPress используют некую среднюю концепцию между MODx и WiX, как и большинство других популярных CMS.

Обобщённые результаты сравнительного анализа CMS приведены в таблице 1.

Таблица 1. Сравнительный анализ CMS

Критерий

Сравнения

CMS

Логичность интерфейса

Обратная совместимость версий

Нет обязательной регистрации пакетов

Средства адаптации любых модулей

Прямое подключение скриптов и стилей

Бесплатная лицензия

 

 

WordPress

+

+

+

-

+

+

Joomla

-

-

-

-

-

+

MODx

+

+

+

+

+

+


 

 

Преимущества и недостатки WiX

WiX – это не просто CMS, а полноценная платформа для создания сайта, предоставляющая не только саму CMS, но и хостинг со всем необходимым ПО. WiX использует методику drag-and-drop, которая предполагает манипуляцию всеми элементами сайта, по средствам графического интерфейса, со 100% генерацией кода. Пользователю не предоставляется возможность ручного кодирования[7]. Это может быть достоинством для тех, кто не занимается WEB-разработкой профессионально, но имеет задачу разместить свой несложный сайт в Интернете с наименьшими затратами, не углубляясь в безопасность и персонализацию. Например, при создании сайта-визитки.

 

Рис. 8 Внешний вид интерфейса WiX

WiX не подойдёт для сложных и крупномасштабных проектов, так как она не имеет возможности для настройки и разграничения прав, персонализации бэкэнда, адаптации собственных модулей, а так же при её применении возникают большие сложности с поисковой оптимизацией (SEO).

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

Выбор технологии серверного программирования

При выборе языка программирования для серверной части сайта следует учесть несколько факторов. Во-первых, сайт может быть написан как с использованием CMS, так и без неё. В первом случае – язык программирования выбирается исходя из требований нужной CMS. Во-вторых, на некоторых WEB-серверах могут быть только определённые интерпретаторы для конкретных языков программирования. В-третьих, некоторые WEB-сервера работают под управлением определённых ОС.

Рассмотрим пример:

  • Выбираем CMS для создаваемого сайта, допустим выбрана Kentico CMS;
  • Данная CMS использует технологию ASP.NET, а значит – используется WEB-сервер Microsoft IIS;
  • WEB-сервер Microsoft IIS работает под управлением ОС Windows;

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

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

Немаловажным фактором является и ОС, на которой будет работать WEB-сервер, так как неправильная настройка и возможные уязвимости в её политики безопасности могут поставить под угрозу работу ни только самого сайта, но и конкретного физического сервера или целой подсети. Подробнее об этом в пункте защиты сайтов.

По данным на 2015 год, популярность языка PHP состовляет около 80%. Такая высокая популярность обусловлена относительной простотой данного языка, в сравнении с большенством других популярных альтернатив (например, ASP.NET). Также, столь высокая популярность способствует тому, что наибольший выбор CMS, на сегодняшний день, имеется именно среди PHP-CMS.

Как было сказано выше, PHP – самый популярный язык серверной части WEB-сайтов. Это весьма весомый аргумент в пользу его выбора.

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

Несмотря на популярность PHP, для некоторых задач выгоднее использование альтернативных языков (например, для написания WEB-скрепперов часто используется Python).

Стоит упомянуть, что на сегодняшний день начинает набирать популярность серверный вариант JavaScript (Node.js). Так как JavaScript используется на стороне клиента, практически без альтернатив (технология java applet сильно устарела), то популярность серверного варианта данного языка может сильно возрасти за счёт того, что разработчики серверной и клиентской частей начнут лучше понимать друг друга, что приведёт к облегчению работы над проектом.

Задача защиты сайта

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

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

Общий алгоритм написания SQL-инъекции выглядит так:

Практически на всех сайтах есть поля для ввода информации, их можно использовать для SQL-инъекции. Рассмотрим простой вариант. Серверный скрипт строит запрос к базе данных:

Затем скриптом дописывается завершающая часть запроса. Далее вписывается закрывающая кавычка и точка с запятой (‘;), после чего вписывается внедряемый запрос, а потом ставится знак SQL-комментария – два коротких тире (--) для игнорирования остальной части SQL-запроса[1]. Таким образом сервер БД получит следующий запрос:

 

Рис. 9. Схематичное изображение SQL-инъекции

Задача защиты от SQL-инъекций является одной из наиболее сложных и важных, так как не существует универсального 100% способа закрытия данной уязвимости. Существуют некоторые стандартные способы защиты, однако они работают не во всех ситуациях. Например, в поле, где должен вводится возраст, логично ожидать целое число, а значит можно преобразовать введённые в поле данные в целое число, перед тем, как отправить запрос серверу БД. Если преобразование завершится неудачно, то передастся пустое значение или ошибка, что не даст возможности внедрить неправомерный код в данное поле. Однако, не везде заранее известен тип вводимых данных (например – комментарии), там могут содержаться различные символы, и данный способ не подойдёт.

Но даже если БД защищена относительно хорошо, в некоторых ситуациях можно внедрить в запрос команды для выполнения в самой ОС, что может поставить под удар ни только конкретную БД, приложение и/или сайт, но и весь сервер или целую подсеть. Зачастую может быть так, что через некоторые WEB-сервера или сервера БД можно отправлять команды ОС, и при этом эти сервера запущены под правами супер-пользователя (администратора), таким образом можно получить доступ ко всем функциям ОС. Защитой в этом случае является настройка и разграничение прав в ОС, в том числе и запуск программ-серверов с неполными правами.

Также проблемой является узнаваемость большого числа используемых технологий. Например, узнав CMS, на которой написан сайт, можно попытаться пройти через характерные для неё уязвимости, та же ситуация – если известно достоверное наименование WEB-сервера или языка серверной части. Необходимо шифровать такие данные, подменяя характерные признаки использования конкретных технологий. Например, переписать стандартные пути к панели управления CMS, сменить логотипы и так далее.

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

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

Рекомендации

  • Запускать программы-сервера с ограниченными правами;

  • Преобразовывать введённые данные, если параметры заранее известны;

  • Использовать хранимые процедуры в SQL;

  • Скрывать модель CMS;

  • Скрывать модель WEB-сервера;

  • Привязывать некоторые ресурсы к конкретным IP;

Задача переноса сайта

В общем случае, сайт содержит структуру, расположенную на сервере, и БД. Рассмотрим общий универсальный алгоритм переноса:

  1. Очистить все кэши сайта;
  2. Скопировать структуру сайта на новый хостинг;
  3. Экспортировать таблицы из БД на старом хостинге и вставить в БД на новом;
  4. Переписать параметры подключения к БД на новые, в конфигурационных файлах, в структуре сайта;
  5. Переписать абсолютные WEB-адреса подключаемых файлов (если есть) на новые;
  6. Переписать абсолютные пути на уровне файловой системы на новые;
  7. Преобразовать кодировки (если имеются проблемы с поддержкой);

Кэши требуется очищать для того, чтобы не применялись старые параметры.

Если на новом хостинге предоставляется возможность настройки параметров БД, а также создания и настройки новых пользователей, то можно настроить параметры работы с БД, идентичные тем, что были на старом хостинге. Так же, некоторые WEB-адреса и пути уровня файловой системы, могут быть записаны в БД сайта. Их так же необходимо переписать в соответствии с параметрами нового хостинга.

Данный способ универсален для различных технологий (серверных языков, CMS и так далее). Однако, в некоторых CMS имеются средства, которые позволяют облегчить данную задачу. Например, в CMS MODx можно выполнить следующее: перенести таблицы из старой БД в новую, скопировать файловую структуру сайта на новый хостинг, а потом скопировать в корень перенесённого сайта стандартный установщик MODx. Запустить установку MODx через скопированный установщик и выполнить обновление сайта, прописав параметры, в соответствии с теми, которые должны быть на новом хостинге. Установщик MODx сам перепишет все конфигурационные файлы теми настройками, которые будут заданы при обновлении. Остальная структура сайта и таблицы с записями в БД останутся нетронутыми. Однако, даже при этом способе требуется очистка кэшей, и перезапись путей в самой БД (если есть).

Рекомендации

  • Сохранять полный архив файловой структуры сайта и его БД до конца переноса;

  • По возможности, не менять кодировки;

  • Учитывать регистр клавиатуры при изменении путей к файлам;

Задача адаптации произвольной вёрстки к различным CMS  

Некоторые CMS (например, MODx) могут содержать встроенные средства для создания и адаптации любой произвольной вёрстки, плагинов и так далее. Общий алгоритм этих операций в MODx описан в разделе “Преимущества и недостатки MODx”. Однако, в некоторых CMSможет вообще не быть средств для адаптации собственного кода (например в WiX), а в некоторых – эти возможности присутствуют частично (например в JoomlaWordPress и большинстве других CMS)[2].

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

Рассмотрим пример. Допустим, есть произвольный шаблон, в котором в качестве имени страницы указано “шаблон”. В html коде это выглядит так:

 … - недостающий код. Допустим шаблон предполагается адаптировать для использования в WordPress. В заголовке требуется вывести имя сайта. За выведение названия сайта в WordPress отвечает функция bloginfo. В этом случае, код будет выгладить следующим образом:

Теперь, вместо статичного текста, в качестве заголовка будет выводиться указанное название сайта[6]. Аналогичным образом нужно переделать остальные секции шаблона[3].

Так как в различных темах могут вызываться разные функции CMS, а также добавляться свои, вписывающиеся в правила конкретной CMS, то для адаптации своего шаблона на начальных этапах лучше взять наиболее простой готовый шаблон. Создать страницу, использующей этот шаблон. Посмотреть код, который сформировался на стороне клиента. Это позволит вычислить, в каких секциях шаблона выводится конкретная информация (например, логотип шапки сайта или контент). После этого нужно просмотреть файлы переделываемой темы и найти в них соответствующие HTML-секции. Это позволит вычислить названия функций, отвечающих за вывод конкретной информации, а также передаваемые в них параметры. Далее можно изменить имена селекторов и расположения секций, в соответствии со стилями адаптируемой вёрстки, либо вставить полученные функции в нужные секции HTML-шаблона.

Этот способ универсален, однако довольно сложен, так как названия блоков и селекторов также могут формироваться динамически, и не всегда быть явно видными в серверных скриптах темы. Так же одни и те же блоки могут дублироваться в различных файлах темы, например для надёжности, или вывода разной информации в зависимости от настройки[4]. Например, в одном и том же месте HTML документа может выводиться название или логотип сайта, в зависимости от того, что выбрал пользователь. Функции отвечающие за их вывод могут находиться в разных файлах.

Рекомендации

  • Использовать в качестве примера темы конкретной CMS, шаблон с наиболее простой вёрсткой;
  • Делать архив создаваемой темы, после настройки каждой HTML-секции;
  • До конца создания темы сохранять архив с исходной HTML-вёрсткой;
  • Изучить подробную документацию по конкретной CMS (в сложных ситуациях);

            Алгоритм получения HTML-шаблона из страниц произвольных сайта или шаблонов различных CMS описан в пункте “преимущества и недостатки MODx”. Данный алгоритм универсален, так как браузер не интерпретирует серверную часть, а HTMLCSS и JavaScript, которые генерируются динамически при помощи серверных языков или передаются в готовом виде, для последующий обработки браузером - одинаковы. Языки клиентской части не зависят от того, при помощи какой технологии был получен код на них. Однако, для конкретной страницы могут быть сгенерированы не все блоки вёрстки, например, в шаблоне есть слайдер, а на конкретной странице он отключён, и сервер не сгенерирует его код в ответной клиентской странице. Поэтому, для полноценного получения вёрстки с произвольного сайта необходимо просмотреть как можно больше его страниц, вытащив максимальное число элементов шаблона. Так же, при генерации конкретной страницы могут подключаться стили и скрипты для других элементов сайта, которые не нужны для всей вёрстки в целом. При переносе вёрстки с произвольного сайта, лучше скопировать все дополнительные файлы, ссылки на которые есть в ответной странице. После того, как все файлы подключены корректно, можно преступать к выявлению и последующему удалению ненужных элементов.

Задача унификации и оптимизации вёрстки

Одной из основных проблем при разработке WEB-сайтов является – плохая кроссбраузерность. Зачастую, она может возникать не только из-за того, что некоторые браузеры по-разному понимают определённые правила CSS, но и из-за неоднозначного задания этих правил, а также из-за использования нежелательных конструкций. Наиболее часто встречающийся проблемой вёрстки является – смешение разметки со стилями. После того, как появился язык стилевого оформления CSS (Cascade Style Sheet), стало нежелательным писать стилевые свойства в качестве атрибутов для тегов HTML-разметки. Одним из примеров такой конструкции является 

Также нежелательным является использование стилевых тегов HTML, таких как

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

может отобразиться как таблица с границей разных цветов, сплошной или пунктирной, а может и вообще отобразиться без границ. Для того, чтобы объединять стилевыми правилами конкретную группу элементов (например, ссылки или изображения, относящиеся к конкретной секции шаблона) в CSS существуют классы и идентификаторы. Но даже если стиль необходимо задать конкретному элементу, не создавая для него класса (для упрощения или исключения), правильным заданием стиля будет написание атрибута “style” внутри конкретного тега, а уже в нём – написание стилевых правил.

Пример правильной конструкции:

Пример неправильной конструкции:

Несмотря на то, что в правильном примере, стили заданы прямо в теге, это не является нежелательной конструкцией, так как все атрибуты, находящиеся внутри “style”, интерпретируются по обычным правилам CSS, таким образом стили и разметка разделены. Неправильный пример использует устаревшую конструкцию задания стилевых атрибутов непосредственно в теге. Вот ещё один пример, но уже с использование нежелательных тегов:

А вот – правильный эквивалент:

Все вышеупомянутые неверные примеры, в которых используется смешение стилей и разметки, были актуальны во времена, когда не было CSS, и функцию стилей также брал на себя HTML. Стилевые возможности HTML - менее гибкие, чем у CSS и оставлены только для большей совместимости и постепенного обновления сайтов. Их использование приводит к плохой кроссбраузерности и неоднозначной интерпретации. Интерпретация стилевых правил происходит по следующим приоритетам:

  1. Смотрятся внешние файлы CSS. Если в них не указано правил для конкретных селекторов, то смотрится тег  на конкретной странице.
  2. Если на конкретной странице нет тега  или в нём не указаны правила для конкретного селектора, то смотрится атрибут “style” внутри конкретного тега.
  3. Если при просмотре стилей, правило для конкретного селектора найдено, например, и во внешнем файле, и на конкретной странице, то сработает второе, так как оно просматривается позже и имеет более высокий приоритет. Таким образом – самый низкий приоритет у стилей во внешних файлах, а самый высокий – у тех, которые внутри тегов. Это даёт возможность задавать стили конкретным страницам и объектам, не редактируя общие правила CSS. При этом, стилевые теги HTML (нежелательные), в большинстве браузеров имеют более высокий приоритет, чем CSS-стили с любым приоритетом, таки образом поиск возможных стилевых ошибок в вёрстке усложняется.

Ещё может возникнуть необходимость использования тегов не по прямому назначению. Например, для создания расписной рамки (границы) у фото или слайдера. Несмотря на то, что в CSS 3 появилась возможность создания рисованных рамок, путём применения свойства “border-image”, рамка может иметь непериодический орнамент, что не даёт возможности отобразить её как череду повторяющихся изображений, а вышеупомянутое свойство работает именно по этому принципу. В таком случае используется свойство “background-image”. Оно поддерживается и в более старых версиях CSS. Для того, чтобы сделать фон рамкой, нужно сделать блок, в котором будет размещено изображение. Высота и ширина изображения в этом блоке должны быть заданы с учётом габаритов рамки).

Также могут возникнуть ситуации, когда требуется нетипичная роль объекта в вёрстке. Например, если у того же фона-рамки есть неровные углы, которые нельзя задать простым закруглением изображения. В этом случае, нужно сделать фон поверх изображения. Для того, чтобы это сделать, в тег <img>, в котором обычно находится само искомое изображение, нужно прописать постоянный путь к рамке, а путь к самому изображению будет прописан в стилевом свойстве “background-image”. Не типичность заключается в том, что фон будет в роли искомого изображения, а само изображение – в роли фона.

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

Рекомендации

  • Не смешивать стили, скрипты и разметку;
  • Не использовать устаревшие и нежелательные конструкции;
  • Полностью задавать параметры для конкретного свойства;
  • Оборачивать объекты в отдельные блоки;

Заключение

В рамках данной статьи были рассмотрены следующие типы задач:
  • Задача выбора технологии серверного программирования;
  • Задача выбора CMS;
  • Задача зашиты сайта;
  • Задача адаптации произвольной вёрстки под различные CMS;
  • Задача переноса сайта;
  • Задача унификации и оптимизации вёрстки;

Приложение 1 общий принцип взаимодействия с сайтом в Интернет

При обращении к какому-либо сайту, его доменное имя должно быть преобразовано в IP-адрес. Доменное имя сайта привязывается к IP-адресу конкретного сервера. Для того, чтобы соотносить доменные имена с IP-адресами, существуют сервера DNS (Domain Name System). Домены разделяются на уровни. Домены первого уровня находятся в конце адреса сайта, например RU, COM, РФ и т.д. DNS-сервера, отвечающие за домен определённого уровня, имеют адреса серверов, отвечающих за домены следующего уровня. Например, требуется попасть на сайт video.yandex.ru (протокол не рассматривается). После того, как пользователь ввёл запрос в адресную строку браузера, происходит следующая последовательность действий:
  1. Компьютер обращается к DNS-серверу, который настроен по умолчанию для выхода в Интернет;
  2. Данный DNS-сервер обращается к DNS-серверу, отвечающему за домен первого уровня (в данном случае – ru);
  3. DNS-сервер из пункта 2 знает адреса серверов, которые идут на следующем уровне, и обращается к нужному DNS-серверу, отвечающему за домен второго уровня (в данном случае – yandex);
  4. DNS-сервер из пункта 3 аналогичным образом обращается на следующий уровень к серверу – video;
  5. Получив IP-адрес сервера, на котором находится искомый сайт, запрос пользователя попадает на этот сервер. В пакете с запросом присутствует номер порта, а также IP-адрес отправителя запроса;
  6. Искомый сервер понимает, по какому протоколу обрабатывать пришедший запрос, с помощью номера порта (например, для HTTP – это порт 80);
  7. После обработки запроса и генерации ответной страницы (см. раздел “Описание общих принципов работы сайта”), результат отправляется на адрес отправителя, указанный в пакете с запросом.
На рисунке ниже, представлена общая иерархия работы DNS-серверов.

 

Рис. 10 Иерархия работы DNS-серверов

Для того, чтобы снизить нагрузку на DNS-сервера, существуют несколько способов. Во-первых, в большинстве ОС существует файл, в котором содержится локальная таблица соотношения доменных имён с IP-адресами. Например, в Windows – это файл hosts, находящийся по адресу C:WindowsSystem32driversetc (где C: - имя системного диска). Во-вторых, по умолчанию большинство браузеров кешируют IP-адреса сайтов, и при следующем обращении смотрят туда. Общая схема приоритетов следующая: при обращении к сайту смотрятся адреса из кэша браузера, если есть нужный адрес, запрос отправляется по нему, если нет – смотрится файл с локальной DNS-таблицей (например – hosts), если нужного адреса нет и там, запрос отправляется по цепочке DNS-серверов.

У данного подхода есть несколько минусов. Во-первых, если в кэше браузера, или в локальном файле DNS-таблиц присутствует адрес сайта, который уже не актуален, то компьютер не будет посылать запрос на новый адрес с этим доменным именем, а будет ошибочно попадать на ненужный ресурс. Этот принцип используется и в некоторых вредоносных программах. Например, для блокировки социальных сетей или других сайтов. Так же вредоносное ПО может прописать адрес сайта-имитатора, например, крадущего данные при авторизации. Пользователь будет набирать верное доменное имя, и ничего не подозревая попадать не туда. Для того, чтобы этого избежать, можно создать в браузере несколько закладок, с часто посещаемыми сайтами, указав в прямую их IP-адреса. В таком случае компьютер не будет ничего соотносить, а сразу отправит запрос в нужное место. Узнать IP адреса сайтов можно, например на сайте 2ip.ru.

Приложение 2 . Рекомендации по выбору и настройке локальной среды

Существуют множество программ, эмулирующих работу WEB и БД серверов. Можно поставить и настроить сервера отдельно, а можно установить готовые решения, содержащие WEB-сервер, сервер БД и интерпретаторы языков серверной части. По причине наибольшей популярности PHP, будут рассмотрены готовые среды с PHP-интерпретатором. У таких готовых сред, как правило есть следующий базовый набор средств для разработки:
  • WEB-сервер (как правило – Apache или nginx);
  • СУБД (как правило – MySQL);
  • PHP-интерпретатор;
У большинства подобных программ имеется именно этот набор. Будут рассмотрены средства для Windows, по причине наибольшей популярности данной ОС. Три наиболее популярных пакета локальных WEB-серверов – это Denwer, Winginx и XAMPP.
  1. Denwer – имеет базовый функционал, включающий WEB-сервер Apache, интерпретатор языка PHP и СУБД MySQL. Также имеет утилиту для администрирования БД – phpMyAdmin. Одним из преимуществ Denwer является работа через виртуальный диск. По умолчанию Denwer спрашивает имя (букву) виртуального диска - прямо во время установки. При запуске установленного Denwer с любого носителя, смонтированного под любым именем, и находящегося в любой папке, он будет подключать к Windows виртуальный диск, который будет иметь имя, указанное при установке. Таким образом, как правило не возникает проблем при работе за разными ПК с переносных носителей (например, с флэш-накопителей). Единственной рекомендацией является – называть виртуальный диск буквой B, так как он - не занят в большинстве случаев.
  2. XAMPP – помимо базового функционала (как и у Denwer), XAMPP дополнительно имеет множество других возможностей. Однако, XAMPP имеет существенный недостаток – отсутствие виртуального диска, что приводит к невозможности работы с переносными носителями, или быстрого переноса на другой ПК, так как многие сайты имеют скрипты с абсолютной адресацией на уровне файловой системы (см. раздел “Задача переноса сайта”). Также в настройках параметров XAMPP прописана информация о пути к папке с установленной программой, поэтому её перемещение потребует дополнительных настроек и для самой программы.
  3. Winginx – среда разработки с WEB-сервером nginx. Имеет весь базовый функционал и ряд дополнительных возможностей, подробнее можно прочесть на официальном сайте http://winginx.com/. Winginx также не создаёт виртуальных дисков, таким образом имеет те же последствия, что и XAMPP.
После установки какой-либо локальной среды, могут возникать типичные проблемы:
  1. Не запускается WEB-сервер. Чаще всего это вызвано тем, что нужный порт занят другой программой (см. раздел “Общий принцип взаимодействия с сайтом в Интернет”. Для устранения этой проблемы требуется освободить нужный порт или изменить настройки номера порта по умолчанию. Однако, любой HTTP-запрос по умолчанию отправляется по 80-ому порту, поэтому, если изменить этот номер, то в адресной строке потребуется указать номер порта. Например, ели изменить номер порта для HTTP с 80 на 999, то полная строка запроса на локальный хост будет выглядеть так – http://localhost:999
  2. Аналогичная ситуация может произойти при работе сервера БД, правда в этом случае нужно указать номер порта в конфигурационных файлах сайта, в параметрах подключения к БД. Как правило – это не доставляет неудобств, так как, в отличии от адреса сайта, его не придётся писать каждый раз.
Еще один аспект, который необходимо учесть – это WEB-адрес локального сайта. Если сайт предполагается показывать на разных ПК, с переносных носителей, то лучше хранить его структуру в папке, которая настраивается по умолчанию для конкретного локального сервера. То есть, к которой сервер обратится по адресу Localhost или 127.0.0.1, так как для входа на этот адрес не требуется менять стандартные настройки файла hosts. Это актуально, так как на конкретном компьютере, пользователю может быть не разрешено менять конфигурацию этого файла.

Библиографический список:

1. Учебные материалы “SQL-инъекция. Оборона и нападение” интернет ресурса SpecialistTV [электронный ресурс] URL: https://www.youtube.com/channel/UCMHzbSZjFdgqh8pndSnSp8Q (дата обращения: 17.09.2015)
2. Горнаков С. Г. Осваиваем популярные системы управления сайтом. ДМК-Пресс, 2009. 333 с.
3. Уильямс Б., Дэмстра Д., Стэрн Х. WordPress для профессионалов. Разработка и дизайн сайтов. Питер, 2014. 461 c.
4. Дэн Рамел Joomla! для профессионалов. ИД "Вильямс", 2014. 441 c.
5. Официальный русский сайт CMS MODx [электронный ресурс] URL: https://modx.ru/ (дата обращения: 17.09.2015)
6. Официальный русский сайт CMS WordPress [электронный ресурс] URL: https://ru.wordpress.org/ (дата обращения: 25.09.2015)
7. Официальный русский сайт CMS WiX [электронный ресурс] URL: http://ru.wix.com/ (дата обращения: 15.10.2015)




Рецензии:

24.06.2016, 21:13 Эрштейн Леонид Борисович
Рецензия: Несмотря на то, что общие рекомендации в статье отсутствуют и не ясна научная новизна, точнее ее нет, я считаю, что статью можно публиковать после следующих доработок.1. Привести сравнительную таблицу рассмотренных движков. 2. Указать тип лицензии этих движков. Тогда станет ясен вклад авторов. Но статья хорошая и работа проделана большая.

25.06.2016 16:16 Ответ на рецензию автора Гутовский Дмитрий Игоревич:
Отредактировано. Обобщённая таблица и сведения о типе лицензии находятся в разделе "Преимущества и недостатки MODx"

25.06.2016, 21:57 Эрштейн Леонид Борисович
Рецензия: Вот теперь можно публиковать. Ну хотя бы так.

28.06.2016, 10:42 Маслов Владимир Алексеевич
Рецензия: Автором проведен сравнительный анализ различных CMS, и в целом материал заслуживает опубликования. Однако, много вопросов вызывает Таблица 2. Во-первых, не указан источник данных, которые приведены в таблице. Во-вторых, не совсем понятно, с чем идет сравнение. На мой взгляд сравнивать вот так в таблице со всеми альтернативами сразу несколько некорректно и бессмысленно. Нужно либо сделать полноценную таблицу, в которой поместить наиболее распространенные языки (Python, Java, JavaScript, Ruby, может быть Perl или что-то еще), либо просто заменить таблицу на несколько общих фраз. Но материал хороший и проделанная работа видна.
28.06.2016 14:14 Ответ на рецензию автора Гутовский Дмитрий Игоревич:
Отредактировано. Таблица 2 удалена и заменена на эквивалентный, по смыслу, текст.



Комментарии пользователей:

Оставить комментарий


 
 

Вверх