Учет требований безопасности при разработке программных продуктов

Часть 2

Алексей Федоров

Моделирование угроз

Ключевые концепции модели угроз

Проверка архитектуры и дизайна на соответствие требованиям безопасности

Проверка кода на соответствие требованиям безопасности

Проверка процесса развертывания на соответствие требованиям безопасности

Дополнительные ресурсы

 

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

Моделирование угроз

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

  • требования по обеспечению безопасности;
  • возможные угрозы;
  • возможные уязвимости и способы их предотвращения.

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

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

Моделирование угроз состоит из пяти основных шагов (рис. 1). Обновление и улучшение модели достигается за счет итеративного повторения шагов с 2-го по 5-й. Детали добавляются по мере продвижения в цикле разработки программного продукта и более внимательного рассмотрения дизайна приложения (см. рис. 1).

 

Рис. 1. Итеративный процесс моделирования угроз

Пять основных шагов, выполняемых при моделировании угроз, приведены в табл. 1.

Таблица 1. Шаги, выполняемые при моделировании угроз

Шаг

Деятельность

Описание

Шаг 1

Определение требований по обеспечению безопасности приложения

Четко определенные требования помогают не только выполнить этот шаг, но и определить объем усилий для выполнения следующих шагов

Шаг 2

Создание общего представления о приложении

Каталогизация основных характеристик приложения и основных типов пользователей («актеров») поможет идентифицировать значимые угрозы

Шаг 3

Декомпозиция приложения

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

Шаг 4

Идентификация угроз

Используя информацию, собранную на шагах 2 и 3, можно идентифицировать угрозы, значимые для приложения, возможных сценариев его использования и контекста, в котором оно будет выполняться

Шаг 5

Идентификация уязвимостей

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

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

В табл. 2 показана входная и выходная информация для каждого шага моделирования угроз.

Таблица 2. Входная и выходная информация для каждого шага моделирования угроз

Входная информация

Шаг

Выходная информация

  • Бизнес-требования
  • Стандарты безопасности
  • Требования по обеспечению стандартов

Шаг 1.  Определение требований по обеспечению безопасности приложения

  • Ключевые требования по безопасности приложения
  • Диаграммы развертывания
  • Сценарии использования
  • Функциональные спецификации

Шаг 2.  Создание общего представления о приложении

  • Диаграмма сценариев развертывания
  • Ключевые сценарии
  • Роли
  • Технологии
  • Механизмы обеспечения безопасности приложения
  • Диаграммы развертывания
  • Сценарии использования
  • Функциональные спецификации
  • Диаграммы потоков данных

Шаг 3. Декомпозиция приложения

  • Границы доверительных областей
  • Точки входа
  • Точки выхода
  • Потоки данных
  • Общие угрозы

Шаг 4.  Идентификация угроз

  • Список угроз
  • Общие уязвимости

Шаг 5. Идентификация уязвимостей

  • Список уязвимостей

Ключевые концепции модели угроз

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

Таблица 3. Входная и выходная информация для каждого шага моделирования угроз

Концепция

Описание

Моделирование для снижения рисков

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

Инкрементальный подход

Работайте с моделью угроз итеративно — пропуск каких-либо деталей на одной итерации не должен вызывать проблем — эти детали будут добавлены на следующей итерации. Фокусируйтесь на максимальной продуктивности каждой итерации

Контекстная детализация

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

Определение границ

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

Входные и выходные критерии

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

Средства коммуникации и совместной работы

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

Информационная модель на основе паттернов

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

Инженерные решения

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

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

Проверка архитектуры и дизайна на соответствие требованиям безопасности

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

Существует три основных аспекта при проверке архитектуры и дизайна приложения на соответствие требованиям безопасности:

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

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

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

На рис. 2 показаны три основных аспекта проверки архитектуры и дизайна приложения.

 

Рис. 2. Три основных аспекта проверки архитектуры и дизайна приложения

Для выполнения проверки архитектуры и дизайна приложения рекомендуется использовать заранее подготовленные списки проверяемых критериев. Примеры таких списков приводятся в табл. 4 и 5. На основе стандартных списков вы можете создавать собственные, в которых будут отражаться проверяемые критерии, специфичные и уникальные для разрабатываемого приложения.

Таблица 4. Пример критериев для развертывания и инфраструктуры

Да/нет

Описание

Дизайн соответствует стандартам безопасности, принятым в компании

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

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

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

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

Идентифицированы структуры доменов, удаленных серверов приложений, серверов баз данных

При дизайне определены требования по кластеризации

Известны возможности безопасных коммуникаций, предоставляемые платформой и используемые приложением

При дизайне учтены требования по масштабируемости и производительности создаваемого приложения

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

Таблица 5. Пример критериев для анализа архитектуры и дизайна
приложения и проверки ввода

Да/нет

Описание

При дизайне определены все входные точки и границы «доверия»

При любом вводе данных вне границы «доверия» выполняется проверка введенной информации

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

При необходимости используется централизованная модель проверки вводимых данных

Используемая стратегия проверки вводимых данных является модульной и согласованной

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

Вводимые данные проверяются по типу, длине, формату и соответствию диапазону допустимых значений

При возможности игнорируются имена файлов и полные имена каталогов

При дизайне учтены возможные атаки типа SQL Injection

При дизайне учтены возможные атаки типа кроссскриптинга

Проверка вводимой информации не ограничивается проверкой ввода только на клиенте

Стратегия проверки вводимых данных распространяется на передачу данных между звеньями и уровнями приложения

Проверка кода на соответствие требованиям безопасности

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

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

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

Анализ кода состоит из четырех шагов, показанных на рис. 3, а их суть описана ниже:

  1. Определение целей анализа кода на предмет безопасности. На этом шаге определяются цели и ограничения для анализа кода.
  2. Выполнение первичного анализа. Используйте первичный статический анализ для обнаружения начального набора ошибок и выявления того, где вероятнее всего будут обнаружены ошибки на следующих итерациях.
  3. Поиск проблем, связанных с безопасностью. На этом шаге выполняется более тщательная проверка кода для обнаружения уязвимостей в системе безопасности, которые чаще всего встречаются в приложениях. Рекомендуется использовать результаты, полученные на шаге 2.
  4. Поиск проблем, связанных с безопасностью, уникальных для архитектуры данного приложения. Окончательный анализ должен быть посвящен поиску ошибок, характерных для архитектуры данного приложения. Этот шаг наиболее важен в тех случаях, когда используются собственные механизмы обеспечения безопасности или механизмы снижения рисков, связанных с безопасностью (рис. 3).

 

Рис. 3. Шаги, выполняемые при анализе кода

В табл. 6 приведена входная и выходная информация для каждого шага деятельности, связанной с анализом кода.

Таблица 6. Входная и выходная информация для каждого шага деятельности,
связанной с анализом кода

Входная информация

Шаг

Выходная информация

  • Требования по безопасности приложения
  • Код, включая список изменений с момента предыдущего анализа
  • Ограничения

Шаг 1. Определение целей анализа кода на предмет безопасности

  • Цели анализа кода
  • Код
  • Цели анализа кода

Шаг 2. Выполнение первичной проверки

  • Список уязвимостей
  • Список областей кода для детального анализа
  • Код
  • Цели анализа кода
  • Список областей кода для детального анализа

Шаг 3. Поиск проблем, связанных с безопасностью

  • Список уязвимостей
  • Код
  • Цели анализа кода

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

  • Список уязвимостей

При анализе кода необходимо придерживаться следующих рекомендаций для достижения наиболее эффективных результатов:

  • анализ блоков кода  это механизм, используемый для пошаговой проверки логических условий в коде. На первом шаге анализа идентифицируются блоки, содержащие циклы, конструкции switch, if, try/catch. На втором шаге выясняются условия, при которых выполняется каждый программный блок. На третьем шаге анализируется следующий блок кода;
  • анализ потоков данных  это механизм, используемый для трассировки данных от точки ввода до точки вывода. Так как в приложении может быть множество потоков данных, используйте список целей анализа кода и список областей кода для детального анализа, составленные на шаге 2. Процесс анализа выглядит следующим образом:

- для каждой точки ввода определите уровень доверия к источнику данных;

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

- перейдите к следующей точке входа.

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

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

Таблица 7. Примеры проблемных областей

Проблемная область

Что искать в коде

SQL Injection

Атака типа SQL Injection происходит, когда вводимые данные могут модифицировать SQL-запрос. При анализе кода обращайте внимание на проверку данных, используемых в SQL-запросах, или используйте параметризованные SQL-запросы

Межсайтовый скриптинг

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

Доступ к данным

Обращайте внимание на способы хранения строк соединения и корректное использование механизмов аутентификации при обращении к базе данных

Проверка вводимых данных

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

Аутентификация

Ищите «короткие» пароли, информацию в виде открытого текста, долгие сессии и другие проблемы, связанные с аутентификацией

Авторизация

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

Конфиденциальные данные

Обращайте внимание на некорректное обращение с конфиденциальными данными — раскрытие секретов в сообщениях об ошибках, коде, памяти, файлах или при передаче по сети

Небезопасный код

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

Неуправляемый код

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

«Встроенные» секреты

Ищите «встроенные» в код секреты в коде и именах переменных — «key», «password», «pwd», «secret», «hash», «salt» и т.п.

Обработка ошибок

Обращайте внимание на функции без обработчиков ошибок и исключений и на пустые блоки catch

Web.config

Изучите настройки конфигурации в файле Web.config, обращая внимание на защиту идентификации на основе web-форм

Безопасность доступа к коду

Просканируйте код на предмет использования asserts, запросов на связывание кода и наличие allowPartiallyTrustedCellersAttribute (APTCA)

Использование шифрования

Обращайте внимание на корректное использование программных интерфейсов, обеспечивающих шифрование

Недокументированные программные интерфейсы

Большинство недокументированных программных интерфейсов должно быть удалено из окончательного кода приложения — обычно такие интерфейсы используются только для отладки приложения

Использование потоков

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

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

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

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

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

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

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

Проверка процесса развертывания на соответствие требованиям безопасности

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

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

Анализ конфигурации и связанных с этим вопросов должен проводиться по категориям, показанным на рис. 4.

 

Рис. 4. Категории для анализа процесса развертывания

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

В табл. 8 приведены категории безопасности сервера, которые следует использовать в качестве основы для анализа процесса развертывания.

Таблица 8. Категории безопасности сервера, которые следует использовать
в качестве основы для анализа процесса развертывания

Категория

Подход

Обновления и «заплатки»

Первым шагом является определение механизмов установки обновлений и «заплаток»

Учетные записи

Учетные записи позволяют аутентифици-рованным пользователям получить доступ к компьютеру. Такие учетные записи должны подвергаться аудиту. Создавайте учетные записи с минимальными привилегиями — таким образом можно будет избежать попыток повышения привилегий. Удалите все неиспользуемые учетные записи. Для предотвращения прямых атак и попыток подбора пароля используйте «длинные» пароли. Протоколируйте ввод неверных паролей

Аудит и протоколирование

Аудит — один из самых важных механизмов обнаружения попыток проникновения и атак. Используйте средства аудита на сервере. Для устранения проблем с безопасностью используйте событийные и системные протоколы

Файлы и каталоги

Защитите все файлы и каталоги с помощью механизмов ограничения доступа. Используйте аудит для определения подозрительных или неавторизованных действий

Порты

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

Протоколы

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

Реестр

Большая часть установок, связанных с обеспечением безопасности, хранится в реестре. Использование Windows Access Control Lists (ACL) позволит, например, блокировать удаленное администрирование реестра

Сервисы

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

Файловый доступ

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

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

Дополнительные ресурсы

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

 

В начало В начало

КомпьютерПресс 4'2007

Наш канал на Youtube

1999 1 2 3 4 5 6 7 8 9 10 11 12
2000 1 2 3 4 5 6 7 8 9 10 11 12
2001 1 2 3 4 5 6 7 8 9 10 11 12
2002 1 2 3 4 5 6 7 8 9 10 11 12
2003 1 2 3 4 5 6 7 8 9 10 11 12
2004 1 2 3 4 5 6 7 8 9 10 11 12
2005 1 2 3 4 5 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
Популярные статьи
КомпьютерПресс использует