Безопасность (Оценка: +3)

Печать / RSS-лента
[/b]

[b]Предисловие

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

Уровни безопасности
Многие программисты задумываясь о безопасности системы представляют единый, универсальный механизм, позволяющий защитить всю систему путем вызова (на пример) функции toDoWell или метода $security->work(), но к сожалению, такой подход не оправдывает себя, в результате получается комок грязного кода, который не защищает систему совсем. Подобная проблема имеет те же корни, что и все комки грязного кода в программировании - божественный объект. Данный антипаттерн говорит, что не следует использовать один механизм для решения несвязанных задач, даже если во всех задачах присутствует слово - безопасность.

Давайте, предварительно, разделим возможные опасности, которые грозят системе, на части:
1. Аппаратный отказ - выход из строя серверов, линий связи, DDoS и т.д. Никакой скрипт вам тут не поможет, нужно либо правильно настраивать сервер, либо обращаться за помощью к администраторам. Об этой проблеме в этой статье мы говорить не будем;
2. Подмена данных - отсутствие верификации (фильтрации) поступающих от пользователя данных, что часто приводит к xss, sql inj и другим не приятным проблемам;
3. Доступ - получение пользователем права выполнять операцию, которую ему выполнять нельзя, на пример изменять пароль другого пользователя;
4. Видимость - данный вид опасности наименее опасный, он скорее служит для улучшения восприятия информации пользователем. Сюда относится показ разным пользователям разной информации, на пример администратор видит рычаги управления системой, а пользователь только упрощенный интерфейс без лишних кнопок.

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

Доступность
Если мы взглянем на клиент-серверную систему (web сайт) как на клиент-серверную систему, то заметим, что здесь участвует два механизма: клиент - который передает данные; и сервер - который эти данные принимает и обрабатывает. Передаваемые от клиента серверу данные назовем командой. Так, команда addUser Baskka 123 переданная на сервер позволяет создать нового пользователя с логином Bashka и паролем 123, а команда removeUser Bashka удалить этого пользователя. Имя команды и передаваемые с ней параметры назовем семантикой команды. Зная семантику команды, мы можем определить данные, которые эта команда должна принимать, и данные, которые эта команда принимать не должна. На пример команда addUser в первом параметре должна принимать логин пользователя, при этом необходимо заранее определить, какие символы могут входить в логин, а какие нет. На пример мы запретим пользователям регистрировать учетные записи с символами, отличными от A-Za-z_0-9. Чем меньше символов могут использовать пользователи (особенно системных), тем защищеннее получится система. Попробуйте вставить sql inj или xss используя только A-Za-z0-9 ;) Назовем доступный набор символов параметра его маской верификации. Верификацией же назовем процесс проверки данных на соответствие маске верификации. Если данные содержат символы, которых нет в маске верификации, верификация считается не пройденной.

Сольем полученное:
1. Команда - обращение к серверу;
2. Параметры команды - передаваемые серверу данные;
3. Маска верификации параметра - символы, которые могут входить в параметр;
4. Верификация параметра - проверка параметра на предмет наличия в нем символов, отсутствующих в маске верификации.

Запишем команду в более расширенном виде:
Login = [A-Za-z_0-9]
Pass = [A-Za-z_0-9*?]
addUser Login Pass
Видно, что команда принимает только определенные данные. Если мы попробуем передать в нее данные недопустимого типа, система вернет ошибку без вреда для себя.

Как реализовать этот подход на практике? В моем случае все реализовано следующим образом:
Существуют классы данных: Integer, String, Float, Alias, FileName, FileAddress и т.д. Каждый класс включает в себя метод isReestablish, который принимает данные и выполняет их верификации в соответствии со своей маской. На пример маской верификации для Boolean является: true|false
Каждый модуль имеет свой контроллер, при чем пользователь может вызывать только методы этих контроллеров. Каждый метод контроллера имеет параметры (как и любой другой метод класса), при чем эти параметры строготипизированы, на пример:
class Controller{
public function addUser(Login $login, Pass $pass){
...
}
}
Когда пользователь передает в систему запрос, он попадает в центральный контроллер. В запросе присутствует следующая информация: имя модуля, имя метода, параметры. Центральный контроллер находит нужный пользователю модуль, получает его контроллер, затем получает метод контроллера и проходя по всем параметрам метода определяет какому типу должны соответствовать данные. Если в процессе этой проверки оказывается, что метод ожидает одни параметры, а пользователь передал другие (путем вызова методы isReestablish), то центральный контроллер возвращает ошибку.

Заметьте, мне не нужно выполнять ручную проверку входящих данные через if и preg_match, все делается автоматически. Так же я могу повторно использовать маски верификации в других контроллерах, а не переписывать код верификации. Безопасность тоже учтена, так как пользователь не сможет передать данные, которые не ожидает метод модуля. Миссия выполнена.

Автор статьи: Артур (28.05.13 / 16:26)
Безопасность, ООП
Рейтинг: +3
Просмотров: 996
Комментарии (10) »