Антипаттерны проектирования: как не нужно писать приложения Андрей Дмитриев andrei-dmitriev@yandex.ru @2008-2011.

Презентация:



Advertisements
Похожие презентации
Проектирование архитектуры ИСО 1. UML 2 Структура определения языка 4.
Advertisements


1. Определить последовательность проезда перекрестка
Таблица умножения на 8. Разработан: Бычкуновой О.В. г.Красноярск год.
Учебный курс Объектно-ориентированный анализ и программирование Лекция 7 Методы как средство реализации операций Лекции читает кандидат технических наук.
1 Знаток математики Тренажер Таблица умножения 2 класс Школа 21 века ®м®м.
Лекция 2 Раздел 2.1 Windows Phone Темы раздела 3.
Программная инженерия Андрей Дмитриев ©
Программная иженерия Андрей Дмитриев ©
Фрагмент карты градостроительного зонирования территории города Новосибирска Масштаб 1 : 6000 Приложение 7 к решению Совета депутатов города Новосибирска.
Кафедра ОСУ, Java 2004 Слайд 1 Наследование Наследование позволяет использовать существующий класс для определения новых классов, т.е. способствует.
Перегрузка операторов x = a + b результат 1-й операнд2-й операнд оператор По количеству операндов операторы делятся на: унарные (один операнд) бинарные.
Урок повторения по теме: «Сила». Задание 1 Задание 2.
Фрагмент карты градостроительного зонирования территории города Новосибирска Масштаб 1 : 6000 Приложение 7 к решению Совета депутатов города Новосибирска.
1 Обработка исключений в Java Одно из важнейших преимуществ Java – разработанный на уровне языка механизм обработки исключений. Исключение в Java - это.
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
1 Знаток математики Тренажер Таблица умножения 3 класс Школа России Масько Любовь Георгиевна Муниципальное общеобразовательное учреждение средняя общеобразовательная.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Набор игр Создание игровых ситуаций на уроках математики повышает интерес к математике, вносит разнообразие и эмоциональную окраску в учебную работу, снимает.
1 Основы работы в интерфейсе Яндекс.Директ Практическое пособие Екатеринбург, 2011.
Транксрипт:

Антипаттерны проектирования: как не нужно писать приложения Андрей

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

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

4 Почему появляются уязвимости? 1.Просчеты при проектировании архитектуры. 2.Ошибки при настройке. 3.Неправильная логика. 4.Небезопасный стиль программирования (антипаттерны). Презентация охватывает антипаттерны.

5 Антипаттерны Антиподы паттернов проектирования. Практики программирования, которых рекомендуется по возможности избегать. Могут существовать в > клиентском коде > библиотеках > Java библиотеках

6 Стилистические антипаттерны «Спагетти». Дублирование кода. «Вшивание» данных (hardcoding). Программирование методом подбора. Раздувание класса. Использование исключений не по назначению.

7 «Спагетти» Характерны функции очень большого размера. Появляется при оценке качества программы с позиции «Программа работает».

8 «Спагетти»: решение Оформлять программу так, чтобы размер функций не превышал десяти-двадцати строк. Использовать метрики для обнаружения "спагетти"-функций (методов). При разрастании функции выделять связные куски кода в отдельные методы.

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

10 Дублирование кода: диагноз Симптомы: программа после изменения в некоторых случаях ведет себя также как и раньше. Возможная причина: были изменены не все места, где используется похожий код. Лечение: > рефакторинг «выделение метода».

11 «Вшивание» данных (hardcoding) Жесткое «вшивание» в программный код различных данных, касающихся окружения программы: > путь к файлу конфигурации > имя почтового сервера > название таблицы БД > и т.д. Программа может работать только в определенных условиях (на компьютере разработчика). Данный антипаттерн невозможно выявить на машине разработчика без досконального изучения кода.

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

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

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

15 Раздувание класса Класс, на который возложено слишком много обязанностей. Большие классы тяжело поддерживать, они очень неповоротливы и не любят рефакторинг. Решение: каждый класс должен иметь одно конкретное назначение, которое можно описать несколькими словами.

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

17 Общие антипаттерны Сломанный диспатч. Некорректные данные. Тип-самозванец. Фиктивная реализация. Недостаточная инициализация.

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

19 Сломанный диспатч: диагноз Симптомы: программа перестала вести себя по-прежнему при внесении изменений в несвязный компонент. Возможная причина: при переопределении метода в классе- наследнике, клиент в соответствии с принципом полиморфизма вызывает новый метод, вместо старого. Лечение: > преобразование типов, > пересмотр набора методов для данного сервиса.

20 Некорректные данные Во время работы программы происходят ошибки в недетерминированный момент времени. При выполнении некоторой конкретной работы приложение работает неправильно.

21 Некорректные данные: диагноз Симптомы: при определенном стечении обстоятельств (точные причины могут быть неизвестны), приложение работает неверно. Возможная причина: приложение пытается использовать некорректные данные, пропущенные на более ранних этапах программы. Лечение: > Проверка данных как можно раньше по времени, > Проверка входных параметров методов.

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

23 Тип-самозванец: пример Класс Form использует строку для выбора алгоритма вычисления площади. public class Form{ String shape; double scale; public Form(String shape, double scale){…} public double getArea(){ if (shape.equals(square)) { return scale*scale; } else if (shape.equals(circle) { return Math.PI*scale*scale; } else {return 0.0;} }

24 Тип-самозванец: пример (cont.) Что произойдет при вызове: Form f = new Form(sqaure); ? Как добавлять новые типы форм в программу?

25 Тип-самозванец : диагноз Симптомы: программа одинаково обрабатывает принципиально различные данные. Возможная причина: для различных типов данных в программе используются теговые поля. Лечение: разделение отличных типов данных на классы.

26 Тип-самозванец: правильное решение public abstract class Form { double scale; public abstract double getArea(); } class Square extends Form { public double getArea(){ return scale*scale; } class Circle extends Form { public double getArea(){ return Math.PI*scale*scale; } public abstract class Form { double scale; public abstract double getArea(); } class Square extends Form { public double getArea(){ return scale*scale; } class Circle extends Form { public double getArea(){ return Math.PI*scale*scale; } Использование различных классов для различных сущностей:

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

28 Фиктивная реализация: пример interface Stack { public Object pop(); public void push(Object o); public boolean isEmpty(); } Как описать пункты: 1. Если объект помещается в стек, то следующий вызов pop() должен его вернуть. 2. Если isEmpty()==true, то обращение к методу pop() должно вызвать исключение. 3. Как выполнять операции на нескольких потоках?

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

30 Недостаточная инициализация Построение экземпляра класса может проходить в несколько этапов (операторов или вызовов методов). Невыполнение некоторых этапов может происходить по причине: > Отсутствия инициализирующего кода, > Отложенной инициализации, > Исключения. Пропущенные этапы могут не влиять на процесс создания, но объект остается в незавершенном состоянии.

31 Недостаточная инициализация: диагноз Симптомы: NullPointerException, неправильные результаты. Возможная причина: конструктор не инициализировал все поля. Лечение: > Правильная инициализация, > Использование значений по умолчанию (вместо null и т.п.), > Использовать метод isInitialized(), > Включение дополнительных (корректных) конструкторов, > Переписать класс.

32 Другие антипаттерны Использование интерфейса как хранилища констант. Несогласованное удаление объекта.

33 Использование интерфейса как хранилища констант Может существовать набор значений, уникальных для всей системы. Вариант сделать их доступными для всех заинтересованных клиентов: > Поместить необходимые константы в интерфейс, > Реализовывать этот интерфейс заинтересованным классом.

34 Использование интерфейса как хранилища констант: пример public interface XConstants { static final int X_PROTOCOL = 11 ; public static final int X_PROTOCOL_REVISION = 0; } public class XBaseWindow implements XConstants { //использование констант без префикса }

35 Использование интерфейса как хранилища констант: недостатки Класс, помимо нужных получает и ненужные константы в область видимости и в качестве API.

36 Хранилище констант: правильное решение Глобальный класс(-одиночка), предоставляющий необходимые константы. Использование static import (с версии JDK1.5.0).

37 Несогласованное удаление объекта Симптомы: программа некорректно управляет ресурсами, не удаляя их или удаляя их несколько раз. Возможная причина: на некоторых путях программы ресурсы не освобождаются один раз.

38 Несогласованное удаление: пример class TableWalker { Connection conn = …; //получаем соединение с БД. … public void walk() throws SQLException { Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(SELECT * FROM student); while (rs.next()) { execute(rs); } conn.close(); }

39 Несогласованное удаление: пример (cont.) class TablePrinter extends TableWalker{ … public void execute(ResultSet rs) throws SQLException { String s = rs.getString(FIRST_NAME); System.out.println(s); … //При инициировании исключения происходит выход из //данного метода и из метода walk() суперкласса. // Переменная conn не освобождается. return; }

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

41 Опасные антипаттерны: С и Java С: > Уязвимости часто связаны с переполнением буфера. Java: > JRE управляет памятью: > Проверка индекса массива > Отсутствие арифметики указателей > JRE часто исполняет непроверенный код. > Нужно предотвращать несанкционированный доступ к ресурсам Результат: различные антипаттерны для языков C и Java.

42 Опасные антипаттерны Предположение о том, что объект неизменяем. Доверие к чужому коду. Некорректное использование public static переменных. Вера в то, что исключение не позволяет создать объект. Уверенность, что исключения безвредны. Восстановление объекта.

43 Предположение о том, что объект неизменяем public class Test { private Object [] array = {new Object(), new Object()}; public Object [] getArray() { return array; } Метод данного класса, возвращающий массив, может предоставить клиенту возможность изменять свое содержимому. Как?

44 Предположение о том, что объект неизменяем: уязвимость Атакующий может помещать в массив свои экземпляры объектов: test.getArray()[0] = new Object();

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

46 Предположение о том, что объект неизменяем: правильное решение public class Test { private Object [] array = {new Object(), new Object()}; public Object [] getArray(){ return java.util.Arrays.copy(array, array.length); }

47 Доверие к чужому коду public RandomAccessFile openFile(final java.io.File f) { askUserPermission(f.getPath()); return(RandomAccessFile)AccessController.doPrivileged(){ public Object run(){ return new RandomAccessFile(f.getPath()); } Некорректные операции, выполняемые чужим кодом могут нарушить работу программы. Данный метод может предоставить доступ практически к любому файлу, игнорируя права пользователя. Как?

48 Доверие к чужому коду: атака public class BadFile extends java.io.File { private int count; public String getPath(){ return (++count==1)? /tmp/foo:/etc/passwd; } Класс переопределяет метод getPath():

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

50 Доверие к чужому коду: решение public RandomAccessFile openFile(File f){ final File copy =f.clone(); askUserPermission(copy.getPath());... return new RandomAccessFile(copy.getPath()); } Не предполагайте что входные данные неизменяемы. Копируйте данные и проводите проверки над ними.

51 Доверие к чужому коду: правильное решение public RandomAccessFile openFile(java.io.File f){ final java.io.File copy =f.clone(); askUserPermission(copy.getPath());... } Метод clone() создает копию атакующего класса. java.io.File copy =new java.io.File(f.getPath()); Правильнее будет копировать немодифицируемые данные (String) и предоставлять доступ на их основе:

52 Некорректное использование public static переменных public class FunctionTable { public static FuncLoader m_functions; } Класс содержит публичную переменную.

53 Некорректное использование public static переменных:атака FunctionTable.m_functions = ; Переменная просто может быть заменена на другую.

54 Некорректное использование public static переменных:проблема Состояние класса может быть изменено неавторизованным кодом. Статические переменные являются глобальными для всего рабочего окружения Java и могут быть использованы для общения между частями системы.

55 Некорректное использование public static переменных: правильное решение (1/2) Уменьшит ь область видимости: static FuncLoader m_functions ; private static FuncLoader m_functions ; Использовать public static только для констант. По возможности использовать enum. Сделать public static поля final (значение final поля не может быть изменено): public class MyClass { public static final int LEFT =1; public static final int RIGHT =2; }

56 Некорректное использование public static переменных: правильное решение (2/2) Определить методы для доступа к изменяемым mutable static полям. Добавить необходимые проверки. public class MyClass { private static byte [] data;; public static byte [] getData((){ return data.clone(); } public static void setData(byte [] b)){ securityCheck(); data =b.clone(); }

57 Вера в то, что исключение не позволяет создать объект public class ClassLoader { public ClassLoader(){ securityCheck(); init(); } Объект не должен существовать если проверка на безопасность не была пройдена.

58 Вера в то, что исключение не позволяет создать объект: атака public class MyCL extends ClassLoader { static ClassLoader cl; protected void finalize(){ cl =this; } public static void main(String [] s){ try { new MyCL(); }catch (SecurityException e){…} System.gc(); System.runFinalization(); System.out.println(cl); } Переопределение метода finalize() позволяет воссоздать объект, на который не существует ссылки.

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

60 Вера в то, что исключение не позволяет создать объект: правильное решение По возможности описывать класс как final. Если метод finalize() может быть переопределен, сделать так, чтобы частично инициализированный объект был нефункционален. Не выставлять значения полей до тех пор пока все проверки не пройдены. Использовать флаг завершения инициализации. public class ClassLoader { private boolean initialized =false; ClassLoader(){ securityCheck(); init(); initialized =true; //check flag in all relevant methods }

61 Уверенность в том, что исключения безвредны Исключения могут содержать данные, выдающие информацию о пользователе (например, имя пути к файлу может содержать имя пользователя). public class PersonalData { public load() throws IOException { String homedir =System.getProperty(user.dir ); File f =new File (homedir,personal.dat ); FileInputStream s = new FileInputStream(f); }

62 Уверенность в том, что исключения безвредны: атака try { personal.load(); }catch (IOException e){ String homedir =parsePath(e.message()); String username =parseUser(homedir); } Атакующий может попробовать выяснить важную информацию.

63 Уверенность, что исключения безвредны: правильное решение public class PersonalData { public load()throws IOException { try {... }catch (Exception e){ throw new IOException(Could not load data ); } Скрывайте информацию, задавая нейтральные значения полей или заменяя исключения.

64 Восстановление объекта public class BigInteger extends Number { private int signum; public BigInteger(int signum,byte [] magnitude)){ if (signum 1){ throw new NumberFormatException(); }... } Класс проверяет значение на допустимость в конструкторе. Если входной параметр не удовлетворяет условию, то создается исключение. Как следствие, выполнение оператора new прерывается и объект не создается.

65 Восстановление объекта: атака Атакующий может создать объект посредством десериализации ложного объекта. ObjectInputStream is = new FileInputStream(badfile.ser); BigInteger bigInt = is.readObject();

66 Восстановление объекта: проблема При десериализации конструктор не выполняется. Атакующий может создать сериализованный объект с неверными значениями полей.

67 Восстановление объекта: правильное решение Создать свой метод readObject() и проводить в нем те же самые проверки, что и в конструкторе. private void readObject(ObjectInputStream s){ s.defaultReadObject(); //Validate signum if (signum 1) throw new StreamCorruptedException(); }

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

69 Ссылки Путеводитель по безопасному программированию: > Bug patterns in Java, Eric Allen > Собрание публикаций по теме: > > > > Antipatterns: Identification, Refactoring and Management, Laplante, Phillip A. and Colin J. Neill. AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis, Brown, William J.; Raphael C. Malveau.

Вопросы?

Антипаттерны проектирования: как не нужно писать приложения Андрей