суббота, 26 февраля 2011 г.

ISSP \ Домен 09. Безопасность приложений. Часть 6

В этой части рассмотрены следующие вопросы:
  • Методология разработки программного обеспечения
  • Концепции объектно-ориентированного программирования
  • Полиморфизм
  • Моделирование данных
  • Архитектура программного обеспечения
  • Структуры данных
  • Связность и связанность


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

Для разработки приложений могут использоваться различные типы языков программирования: машинный язык, язык ассемблера, либо языки высокого уровня. Машинный язык – это тот язык, который понимает процессор, и с которым он может работать напрямую. Ассемблер и языки высокого уровня непосредственно процессору не понятны, поэтому они должны быть транслированы в машинный язык. Этот процесс, как правило, выпоняется компилятором, задачей которого является перевод понятного человеку языка программирования в понятный компьютеру машинный язык (или объектный код).
Исходный код и Машинный код. При обработке компилятором исходного кода программы, на выходе получается объектный код, предназначенный для конкретной платформы и процессора. Этот объектный код является исполняемой формой приложения, которую пользователь покупает у поставщика. Запущенный объектный код представляется в виде машинного кода, понятного процессору.
Поколения языков программирования. Языки программирования развивались на протяжении длительного времени, предоставляя программистам и системам все более широкую функциональность. Поколения языков программирования перечислены ниже:

  • Первое поколение: Машинный язык
  • Второе поколение: Язык ассемблера
  • Третье поколение: Язык высокого уровня
  • Четвертое поколение: Язык очень высокого уровня
  • Пятое поколение: Естественный язык
ПРИМЕЧАНИЕ. Если вам потребуется больше узнать о различных поколениях языков программирования, ознакомьтесь со статьей «Языки программирования» по ссылке: www.logicalsecurity.com/resources/resources_articles.html.
Обычно покупатель приобретает программное обеспечение в виде объектного кода. Это программное обеспечение уже скомпилировано и готово к установке и работе на системе покупателя. Для преобразования программы в объектных код, понятный процессорам определенного типа, производитель программного обеспечения использует компилятор. Программа, которая работает на компьютере с процессором Alpha, может не работать на компьютере с процессором Pentium, поскольку различные типы процессоров требуют различные варианты машинного языка для исполнения программ.

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

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


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

Объектно-ориентированное программирование (ООП) позволяет выполнять те же функции, но по-другому, с использованием более эффективных методов. Вам нужно понимать основные концепции ООП.

ООП использует классы и объекты. Объект реального мира, например, «стол», является членом (или экземпляром) более широкого класса объектов, например, «мебель». Класс «мебель» имеет набор связанных с ним атрибутов (например, цвет, размер, вес, стиль, стоимость), которые при создании наследует объект. Эти атрибуты будут использоваться при создании экземпляра объекта класса «мебель» – например, «стол» или «стул». Объект «стол», являющийся членом класса «мебель», наследует все атрибуты, определенные для данного класса (рис. 9-12).

Рисунок 9-12. В объектно-ориентированном программировании при наследовании каждый объект относится к определенному классу и обладает всеми атрибутами этого класса

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

Но каким образом класс создает объекты на основе запросов? Часть программного обеспечения, написанного на объектно-ориентированном языке, принимает запросы, отправляемые, как правило, другим объектом. Запрашивающий объект хочет, чтобы новый объект выполнял определенные функции. Скажем, объект «А» хочет, чтобы объект «B» выполнил вычитание чисел, отправленных от «А» к «B». При поступлении запроса, создается объект (экземпляр), имеющий весь необходимый программный код. Объект «B» выполняет задачу вычитания и отправляет результат обратно объекту «А». Не имеет значения, на каком языке программирования написаны эти два объекта, важно, что они знают, как общаться друг с другом. Один объект может взаимодействовать с другим объектом с помощью интерфейса прикладного программирования (API) этого объекта. API является механизмом, позволяющим объектам общаться друг с другом. В качестве аналогии можно рассмотреть иностранные языки. Если два человека хотят общаться, они должны говорить на одном языке, например, на русском. Если один из них знает русский плохо (например, знает всего несколько фраз), набор тем для общения будет сильно ограничен. Но если эти люди говорят на разных языках, пообщаться им не удастся.
ПРИМЕЧАНИЕ. Объект – это предварительно подготовленный код, включенный в модуль.
Так что же такого замечательного в ООП, чем методики ООП отличаются от методик "не-ООП"? "Не-ООП" приложения – монолитны. Приложение является просто большой кучей кода. Если требуется что-то изменить в этой куче, нужно будет пройти через все функции программной логики, чтобы понять, к чему может привести это изменение. Если программа содержит сотни или тысячи строк кода, это нельзя назвать простой и приятной задачей. Если же вы решили написать свою программу на объектно-ориентированном языке, у вас не будет одного монолитного приложения, ваше приложение будет состоять из небольших компонентов (объектов). Если нужно будет внести некоторые изменения или обновления в отдельные функции такого приложения, достаточно будет просто изменить код в рамках класса, отвечающего за создание объектов, которые выполняют соответствующую функцию, и не беспокоиться об остальных функциях программы. Ниже перечислены основные преимущества ООП:
  • Модульность
    • Автономные объекты, взаимодействующие посредством обмена сообщениями
  • Отложенная реализация
    • Внутренние компоненты объекта могут быть переопределены без изменения других частей системы
  • Возможность повторного использования
    • Детализация (усовершенствование) классов посредством наследования
    • Использование тех же самых объектов другими программами
  • Естественность
    • Объектно-ориентированный анализ, проектирование и моделирование непосредственно связаны с потребностями и решениями бизнеса
Большинство приложений имеют ряд одинаковых функций. Вместо того чтобы разрабатывать один и тот же код, для использования в десяти различных приложениях, использование ООП позволяет один раз создать объект с соответствующей функциональностью и затем использовать его в других приложениях. Это уменьшает время разработки и экономит деньги.

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

Объекты инкапсулируют значения атрибутов, т.е. эта информация упакована под одним именем и может быть использована как единое целое другими объектами. Объекты должны иметь возможность взаимодействовать друг с другом, и это реализуется с помощью сообщений, отправляемых интерфейсу прикладного программирования (API) принимающего объекта. Если объект «А» должен сообщить объекту «B», что остаток на расчетном счете клиента нужно уменьшить на 100 рублей, он посылает объекту «B» соответствующее сообщение. Это сообщение состоит из ссылки на объект-получатель, ссылки на метод, который должен быть выполнен, а также соответствующих аргументов.

У объекта могут быть общедоступные (shared) и скрытые (private) части. Общедоступные части объекта – это интерфейс (API), который позволяет ему взаимодействовать с другими компонентами. Сообщения поступают через интерфейс, указывая объекту, какую нужно выполнить операцию (метод). Реально работу (запрошенные операции) выполняют скрытые части объекта. Другим компонентам не нужно знать, как устроен каждый объект изнутри, им достаточно знать, какие операции он выполняет. Таким образом осуществляется скрытие информации. Детальная информация о выполнении операций скрыта от всех элементов программы, находящихся вне объекта. Объекты взаимодействуют через четко определенные интерфейсы, поэтому им не нужно знать, каким образом работают другие объекты.
ПРИМЕЧАНИЕ. Скрытие данных обеспечивает инкапсуляцию, которая защищает приватные данные объекта от доступа извне. Нет необходимости в предоствлении полного доступа одних объектов к внутренним данным или процессам другого объекта.
Использование методов объектно-ориентированного программирования позволяет аналитикам и разработчикам взглянуть на более высокий уровень операций и процедур, не просматривая каждый отдельный объект и его код. Такая модульность обеспечивает более наглядную и понятную модель.

Абстракция (abstraction) – это возможность опустить ненужные и неважные детали, которые затем будут реализованы в дочерних классах. Это дает возможность выделения концептуальных аспектов системы. Например, если архитектору программного обеспечения нужно понять, как организованы потоки данных в программе, ему нужно будет разобраться с глобальными, высокоуровневыми частями программы и отследить, какие шаги проходят данные от момента их поступления в программу до момента выхода из нее в виде результатов. Ему было бы трудно понять концепции программы, если бы ему потребовалось анализировать программу на уровне мелких деталей. С помощью абстракции, ненужные детали исключаются, что позволяет работать только с важными частями программы.

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

У каждого объекта должна быть спецификация, которой он должен придерживаться. Такой порядок обеспечивает более чистое программирование, уменьшает количество программных ошибок и недостатков. Приведенный ниже список является примером того, что должно быть разработано для каждого объекта:
  • Название объекта
  • Описания атрибутов
  • Имена атрибутов
  • Содержание атрибутов
  • Типы данных атрибутов
  • Внешний ввод данных в объект
  • Вывод данных из объекта вовне
  • Описания операций
  • Названия операций
  • Описания функциональных интерфейсов
  • Описания работы операций
  • Вопросы производительности
  • Ограничения и лимиты
  • Связь между экземплярами
  • Связь посредством сообщений
Разработчик создает класс, который описывает эту спецификацию. При создании экземпляров объектов, они наследуют эти атрибуты.

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

Объекты могут быть включены в библиотеку, что обеспечивает более экономичный способ их использования несколькими приложениями (см. Рисунок 9-13). Библиотека предоставляет индексы и указатели на объекты, находящиеся в той же или другой системе.

Рисунок 9-13. Приложения находят нужные им объекты в библиотеки с помощью индексов

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

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


Полиморфизм

Полиморфизм – это слово из греческого языка, которое означает «наличие нескольких форм». Понимание этой концепции обычно вызывает сложности, поэтому давайте рассмотрим ее на примере. Если я разрабатываю программу на объектно-ориентированном языке, я могу создать переменную, которая может использоваться для хранения данных различных типов. Приложение будет определять, какой тип данных использовать, непосредственно во время выполнения программы (run time). К примеру, если имя моей переменной USERID и я разрабатываю объект, в котором предусмотрено, что эта переменная может хранить либо целые числа, либо строки, это обеспечивает дополнительную гибкость. С помощью такой переменной, идентификатор пользователя может быть принят в виде числа (номер записи) или имени (строки символов). При этом приложение «А», использующее этот объект, может работать с идентификаторами в виде целых чисел, а приложение «B», использующее тот же объект, может работать с идентификаторами, представленными в виде строки символов.

Полиморфизм - это возможность объектов с одинаковой спецификацией иметь различную реализацию и выдавать различные результаты при получении одинаковых данных. Реализация методов объекта может быть изменена, например, в процессе наследования.
Объектно-ориентированный анализ (OOA – object-oriented analysis) – это процесс определения классов объектов, которые будут подходить для решения. Проводится анализ проблемы для определения классов объектов, которые нужно будет использовать в приложении.
Объектно-ориентированное проектирование (OOD – object-oriented design) создает представление проблемы реального мира и связывает ее с программным решением, используя ООП. Результатом объектно-ориентированного проектирования является проект, который разделяет на модули данные и процедуры. Проект связывает объекты данных и операции обработки.

В предыдущих разделах был в упрощенном виде рассмотрен подход структурированного анализа. Подход структурированного анализа рассматривает все объекты и субъекты приложения и устанавливает взаимосвязи, коммуникационные маршруты и наследуемые свойства. Это отличается от моделирования данных, при котором рассматриваются данные, независимо от способа их обработки и компонентов, обрабатывающих данные. Модели данных отслеживают прохождение данных от начала до конца и проверяют корректность данных на выходе. Объектно-ориентированный анализ является примером подхода структурированного анализа. Если аналитик проводит объектно-ориентированный анализ приложения, он должен убедиться, что все отношения правильно установлены, цепочки наследования предсказуемы и удобны, что экземпляры объектов практичны и предоставляют необходимую функциональность, что атрибуты каждого класса охватывают все необходимые значения, используемые приложением. Если другой аналитик проводит анализ модели данных того же приложения, он будет отслеживать данные и возвращаемые после их обработки значения. Приложение может иметь прекрасную OOA-структуру, но если оно получает задание посчитать 1 +1 и возвращает результат 3, видимо что-то в нем все-таки неправильно. Это именно то, что рассматривает моделирование данных.

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


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

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

Если от приложения требуется, чтобы оно выполняло сканирование жестких дисков и электронной почты на наличие вирусов, архитектура программного обеспечения разобьет это требование на отдельные блоки (модули), которые должны будут реализовать функции этого приложения. Среди таких блоков будут следующие функциональные модули:
  • Хранилище вирусных сигнатур
  • Агент, который выполняет сравнение содержимого файлов с сигнатурами вирусов
  • Процедуры анализа содержимого сообщений электронной почты
  • Процедуры извлечения файлов из архивов
  • Процедуры на случай обнаружения вируса
  • Процедуры на случай выявления зашифрованного вложения электронной почты
Такой способ разработки программного продукта обеспечивает лучшую управляемость и модульный подход к задачам и решениям. Если вы даете группе программистов задание разработать антивирусную программу, они не будут знать, что каждому из них нужно делать конкретно. Однако если одному из них вы даете указание написать фрагмент программы, который будет хранить и обновлять файлы сигнатур, другому – разработать компонент для сравнения файлов с сигнатурами, а третьему – задание по созданию модуля для работы с заархивированными файлами, все программисты получат конкретные задачи, четкие цели и разойдутся по своим рабочим местам для выполнения осмысленной работы.

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


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

Структура может быть простой, как, например, скалярное значение, которое представляет единственный элемент, вызываемый по идентификатору и хранящийся в одной ячейке памяти. Скалярные элементы могут быть сгруппированы в массивы, доступ к которым выполняется по индексам. Другими структурами данных могут являться иерархические структуры, представленные в виде списков со множественными связями, и содержащие скалярные элементы, векторы, и, возможно, массивы. Иерархическая структура обеспечивает категоризацию и взаимозависимость между элементами. На Рисунке 9-14 показаны простые и более сложные структуры данных.

Рисунок 9-14. Структуры данных по своей природе могут быть очень простыми и очень сложными


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

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

Примером низкой связанности может быть передача одним модулем значения переменной в другой модуль. Если модуль «А» передает одно значение модулю «В», другое – модулю «C», и еще одно – модулю «D», это будет являться примером высокой связанности. При этом модуль «А» не может выполнять свои задачи пока не получит результаты от модулей «B», «C» и «D».
ПРИМЕЧАНИЕ. Модули следует разрабатывать самодостаточными и выполняющими единственную логическую функцию, что будет являться высокой связностью. Модули не должны сильно зависеть друг от друга, что будет являться низкой связанностью.

Комментариев нет: