- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
Глава 7. Безопасность Ajax-приложений |
297 |
Бели в процессе настройки разрешено лишь одно обращение к серверу, изменить позицию одного корабля невозможно без очистки всего игрового поля. Таким образом, изменить настройку в свою пользу в принципе невозможно.
Настройка, допускающая единственное обращение к серверу, ограничивает свободу действий нечестного участника, не затрагивая при этом интересы игрока, соблюдающего правила. Если модель на стороне сервера функционирует правильно, API, предполагающий многократные обращения к серверу, не должен предоставить возможность несанкционированного вмешательства, однако число точек входа, в которых такое вмешательство оказывается потенциально возможным, значительно больше. В результате разработчику серверной программы приходится выполнять намного больший объем работы по проверке безопасности.
В разделе 5.3.4 мы говорили о том, что для упрощения API желательно применять образ разработки Fagade. Подобный подход желательно использовать и сейчас, на этот раз для повышения уровня безопасности. Чем меньше точек входа, доступных из Интернета, тем проще реализовать требуемую политику.
Структура API может в какой-то степени защитить приложение от нежелательного воздействия извне, однако некоторые точки входа должны существовать для того, чтобы клиентская программа Ajax могла взаимодействовать с сервером. В следующем разделе мы рассмотрим способы их защиты.
7.4.2. Ограничение доступа к данным из Web
В идеале хотелось бы разрешить доступ к динамическим данным, подготовленным для нашего приложения, Ajax-клиенту (и, возможно, другим авторизованным программам) и запретить его всем остальным. Некоторые технологии, обеспечивающие работу с богатыми клиентами, дают возможность использовать специальные протоколы, однако в Ajax-приложениях наш выбор ограничен HTTP. Как вы уже знаете, защищенный протокол HTTP позволяет скрыть передаваемые данные от постороннего взгляда, но он не позволяет определить, кто обращается по конкретному URL.
К счастью, протокол HTTP обладает большими потенциальными возможностями, а объект XMLHttpRequest обеспечивает дополнительный контроль над ним. Когда запрос поступает на сервер, серверная программа имеет доступ к полям заголовка, из которых можно извлечь информацию об источнике запроса.
Фильтрация HTTP-запросов
Для написания конкретных примеров мы будем использовать язык Java. Существуют и другие технологии, позволяющие добиться аналогичных результатов. При создании Web-приложения на Java мы можем определить объект javax.servlet.Filter, который будет перехватывать запросы. В подклассе F i l t e r переопределим метод doFilter() так, чтобы он анализирован HTTPзапрос перед тем, как принять решение о его передаче по назначению. В листинге 7.5 показан код простого фильтра, который перехватывает запрос,
298 Часть III. Создание профессиональных Ajax-приложений
апосле либо продолжает его обычную обработку, либо появляется страница!
ссообщением об ошибке.
Листинг 7.5. Java-фильтр, используемый в системе защиты
public abstract class GenericSecurityFilter implements Filter { protected String rejectUrl=null;
public void init(FilterConfig config) throws ServletException {
//О Указание URL, соответствующего отвергнутому запросу rejectUrl=config.getInitFarameter("rejectUrl"); }
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//© Проверка допустимости запроса if (isValidRequest(request)){
chain.doFilter{request, response); }else if {rejectUrl!=null){
//0 Обращение к URL, соответствующему отвергнутому запросу RequestDispatcher dispatcher
=request.getRequestDispatcher(rejectUrl) ; dispatcher.forward(request, response); }
}
protected abstract boolean isValidRequest{ServletRequest request);
public void destroy{){}
} |
щ |
||
|
|
|
Фильтр представляет собой абстрактный класс, в котором объявлен абстрактный метод isValidRequest(). Решение о дальнейшей судьбе запроса принимается на основании его анализа. Если в результате вызова метода isValidRequest () © возвращается значение false, запрос перенаправляется по U R L ©, определенному в конфигурационном файле Web-приложения О.
Рассматриваемый здесь фильтр предоставляет возможность создавать конкретные подклассы, т.е. его можно адаптировать для различных стратегий защиты.
Использование НТТР-сеанса
Для поддержки сеанса часто используется следующий подход. При регистрации пользователя создается объект-идентификатор. При последующих обращениях производится проверка на наличие этого объекта. Простой фильтр, решающий данную задачу, показан в листинге 7.6.
Листинг7.6. Фильтрдля проверки идентификатора сеанса
public class SessionTokenSecurityFilter extends GenericSecurityFilter {
protected boolean isValidRequest(ServletRequest request) { boolean valid=false;
HttpSession session=request.getSession() ; if (session!=null){
UserToken token=(Token) session.getAttribute{'userToken');
Глава 7. Безопасность Ajax-приложенип
if (token!-null){ valid=true;
}
}
return valid;
}
} |
я |
Данный подход типичен для традиционных Web-приложений. Если проверка дает отрицательный результат, пользователю предлагается страница регистрации. В Ajax-приложениях ответ сервера может быть более простым по сравнению с традиционными Web-приложениями и содержать данные в формате XML, JSON либо в виде обычного текста. В ответ на получение определенных данных клиент может самостоятельно предоставить пользователю средства регистрации. В главе 11 мы обсудим реализацию средств регистрации в рамках приложения Ajax Portal.
Использование зашифрованных HTTP-заголовков
Существует еще один способ определения корректности запроса. Он предполагает добавление к HTTP-заголовку дополнительного поля и проверку его наличия посредством фильтра. В листинге 7.7 приведен пример фильтра, который ищет конкретное поле и проверяет совпадение зашифрованного значения с ключом, хранящимся на сервере.
Листинг 7.7. Фильтр для проверки поля HTTP-запроса |
"ШВШ! |
||||||
public |
class |
SecretHeaderSecurityFilter |
|
||||
extends GenericSecurityFilter { |
|
||||||
private String headerName=null; |
|
||||||
public |
void init(FilterConfig config) throws ServletException { |
||||||
super.init(config); |
|
|
|||||
// Имя |
поля |
задается |
как |
конфигурационный параметр |
|
||
h e a d e r N a m e - c o n f i g . g e t l n i t P a r a m e t e r ( " h e a d e r N a m e " ) ; |
|
||||||
} |
|
|
|
|
|
|
|
protected |
boolean |
isValidRequest(ServletRequest request) |
{ |
||||
boolean |
valid=true; |
|
|
||||
HttpServletRequest |
hrequest=(HttpServletRequest)request; |
||||||
if |
|
(headerName!=null){ |
|
||||
valid=false; |
|
|
|
||||
// О Получение значения заголовка |
|
||||||
|
String |
|
headerVal=hrequest.getHeader(headerName); |
|
|||
|
Encrypter |
crypt=EncryptUtils.retrieve(hrequest); |
|
||||
|
if |
(crypt!=null){ |
|
|
|||
// © Сравнение значения заголовка |
|
||||||
|
|
valid=crypt.compare(headerVal); |
|
||||
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return valid;
}
^ww |
часть III. иоздание профессиональных Ajax-приложений |
При проверке запроса фильтр читает заголовок с определенным именем
Ои сравнивает его с кодированным значением, содержащимся на сервере
©.Данное значение не постоянно; оно генерируется для каждого конкретЛ ного сеанса, что затрудняет незаконное обращение к системе. Класс ЕпЯ
crypter использует для генерации МХ)5-значения Apache Commons Codec и javax.security.MessageDigest. Полностью набор классов можно полД чить, скопировав коды примеров для данной книги. Принцип формирования ЛГО5-значения в шестнадцатеричном формате показан ниже.
MessageDigest digest=MessageDigest.getInstance("MD5"); byte[] data=privKey.getBytes(); digest.update(data);
byte[] raw=digest.digest(pubKey.getBytes()); byte[] b64=Base64.encodeBase64(raw);
return new String{b64);
где privKey и pubKey — соответственно закрытый и открытый ключ. Чтобы настроить фильтр для проверки всех URL, соответствующих пу-
ти /Ajax/data, надо добавить к конфигурационному файлу web.xml нашего приложения следующее определение:
<filter id='securityFilter_I'> <filter-name>HeaderChecker</filter-name> <filter~class>
com.manning.aj axinaction.web.SecretHeaderSecurityFilter </filter-class>
<init-param id='securityFilter_l_param_l'> <param-name>rejectUrl</param-name> <param-value>/error/reject.do</param-value>
</init-param>
<init-param id='securityFilter_l_param_2'> <param-name>headerName</param-name> <param-value>secret-password</param-value>
</init-param> </filter>
Согласно данной конфигурации отклоненные запросы после проверки значения поля secret-password направляются URL /error/reject.do. Кроме того, мы определяем отображение фильтрации, в результате чего фильтр вступает в действие для любого запроса, соответствующего указанному пути.
<filter-mapping> <filter-name>HeaderChecker</filter-name>
<url-pattern>/ajax/data/*</url-pattern> </filter-mapping>
На стороне клиента для генерации МБ5-дайджеста Base64 использу-
ется |
библиотека Пола Джонстона (она упоминалась ранее в этой гла- |
|
ве). |
Для того чтобы добавить поле HTTP-заголовка, мы вызываем ме- |
|
тод |
setRequestHeadert). |
|
function loadXml(url){ |
||
|
var |
req=null; |
|
if |
(window.XMLHttpRequest){ |
req=new XMLHttpRequest();
Глава 7. Безопасность Ajax-приложений |
301 |
} e l s e if (window.ActiveXObject){
req=new ActiveXObject("Microsoft.XMLHTTP");
}
i f (req)f req.onreadystatechange=onReadyState; req.open(•GET',url,true);
req.setRequestHeader('secret-password',getEncryptedKey()); req.send(params);
}
}
Здесь функция кодирования создает МО5-дайджест Base64 для указанной строки.
var key="password"; function getEncryptedKey(){
return b64_md5(key);
}
Данное решение предполагает передачу Ajax-клиенту значения переменной key. Ключ для сеанса можно передать по протоколу HTTPS при регистрации пользователя. Он должен представлять собой псевдослучайное значение, а не строку символов, например.
Положительная особенность данного решения состоит в том. что поле заголовка HTTP-запроса не может быть модифицировано посредством стандартной гипертекстовой ссылки или HTML-формы. Злоумышленникам придется программировать HTTP-клиент, а это требует определенного уровня подготовки. Очевидно, что по мере роста популярности объекта XMLHttpRequest среди разработчиков информация о том, как сформировать поле заголовка в составе запроса, будет становиться все более доступной. Следует заметить, что средствами, подобными Apache HTTPClient и Perl LWP::UserAgent: эту задачу можно было решить и раньше.
Фильтры и другие средства аналогичного назначения не являются непреодолимой преградой для тех, кто хочет обратиться к вашему узлу, но существенно усложняют эту задачу. Как и у любого разработчика, в распоряжении хакера имеются лишь ограниченные ресурсы, поэтому, защитив приложение несколькими способами, описанными выше, вы существенно снизите вероятность несанкционированного доступа к поддерживаемой вами службе.
На этом мы заканчиваем разговор о защите Ajax-приложений. Существуют вопросы, которые не нашли отражения в данной главе. Мы решили не обсуждать их лишь потому, что они типичны не только для инфраструктуры Ajax, но и для любых Web-приложений. Надежные средства аутентификации и авторизации помогут вам управлять доступом к службам. Стандартные HTTP-заголовки могут быть использованы для определения источника запроса, что затрудняет обращение к службе, минуя официальные каналы (однако не делает такое обращение невозможным). Те, кого заинтересовали вопросы безопасности Ajax-приложений, могут найти более подробные сведения в литературе, специально посвященной этой теме.
И наконец, помните, что безопасность — понятие относительное. Никакая защита не может быть абсолютно надежной. Максимум, чего можно добиться — опережать на шаг злоумышленников. Применение HTTPS и проверку