Перевод статьи Scott Arciszewski «A Gentle Introduction to Application Security».
У меня есть печальные новости для программистов, читающих эту заметку. Но, как вы знаете, нет худа без добра.
Новость первая: если вы являетесь веб-разработчиком или только размышляете о том, чтобы начать изучать веб-разработку, то, вероятнее всего, вы не думаете о себе как о специалисте по безопасности. Может быть, вы принимаете во внимание некоторые угрозы безопасности, например, при валидации пользовательских данных. Но, скорее всего, вы все же не являетесь экспертом в этой области.
И вторая новость: если ваш код выполняется на боевом сервере, он находится на первой линии обороны всей системы и, возможно, всей сети. Поэтому логично, что приложение, которое вы разрабатываете, обязано быть безопасным.
Допустим, вы неправильно обработали включение пользовательских данных в запрос к базе и, как следствие, кто-то сможет получить доступ к приватным данным, хранящимся в базе, или нанести вред вашему приложению. Вы забыли экранировать данные перед их выводом или экранировали их не должным образом — и “внезапно” ваше приложение обернулось против пользователей вашего клиента, заразив их компьютеры вредоносными программами.
В понимании многих разработчиков все это зачастую выглядит как излишнее усложнение работы. Самый распространенный аргумент, который я слышал: “Наши клиенты не платят за безопасность, они хотят новую, красиво реализованную фичу, выпущенную «еще вчера»”.
Я не предлагаю становиться вам экспертом в области компьютерной безопасности. В конце концов, это требует как минимум определенного образования. Но я хочу донести следующую мысль: думать о безопасности приложения — обязанность каждого разработчика. Можно сделать только один шаг навстречу этому и остановиться, и этого будет достаточно для вашей организации и ваших клиентов. Главное — сделать правильный первый шаг.
Чаще всего при изучении вопроса безопасности приложений советуют прочитать списки самых распространенных угроз, такие как OWASP Top10 и SANS Top25. На мой взгляд, эта рекомендация приносит больше вреда, чем пользы. Ведь в реальности существует гораздо больше уязвимостей. Для иллюстрации можно использовать своего рода шутку: “кому-то удалось взломать планету, используя 11 OWASP уязвимость, потому что никто этого не ожидал”.
Злоумышленники мыслят, используя схемы, а не чеклисты. Они ищут простейший способ достижения своей цели, а их цель — находясь снаружи системы, получить привилегированную позицию внутри нее.
Если вы хотите выпустить приложение и не бояться, что кто-то украдет ваши данные, заразит компьютеры ваших пользователей и использует ваши сетевые ресурсы для распространения атак, тогда держать в голове чеклист — вряд ли хорошая идея.
Поэтому я предлагаю взглянуть на уязвимости в системе безопасности под несколько другим углом. По сравнению со списками угроз, описанная ниже модель лучше адаптирована для восприятия новичками, а также развивает тип
Таксономическая модель для описания уязвимостей приложения
Вместо создания чеклиста заданного размера (OWASP Top10, SANS Top25, Paragon Top50 и т.д.) для систематизации уязвимостей приложения можно использовать тот же подход, который используется для классификации живых организмов. Как и организмы, многие уязвимости имеют особенности и общие черты. Задача охватить общей системой все разнообразие живых существ может показаться недостижимой, но, используя высокий уровень абстракции, мы можем объединить их всех в небольшое количество групп. Аналогичный подход можно применить и к уязвимостям. Собственно, это та идея, которой я хочу поделиться в этой статье.
Ниже приведен мой способ деления уязвимостей по категориям. Даже если вы не очень опытный разработчик, изучив каждую из них, вы сможете стать более ценным специалистом, чем тот, кто знает только стратегии защиты в конкретной области, но не понимает фундаментальную проблему уязвимости.
1. Нарушение в обособлении данных от инструкций
Данная категория охватывает большинство уязвимостей, используемых хакерами любого толка, и включает в себя следующие подкатегории:
- Межсайтовые сценарные уязвимости (cross-site scripting)
Воздействуют через веб-клиенты. Данные, некорректно обработанные в приложении, вызывают запуск JavaScript с вредоносный кодом на компьютере пользователя. - SQL-инъекции
Воздействуют на запросы к базе данных. Отправив определенным образом изготовленную строку, злоумышленник может изменить семантику SQL запроса. Во многих случаях SQL-инъекции могут быть также использованы для распространения вредоносных программ. - LDAP-инъекции
Воздействуют на Active Directory аналогично SQL-инъекции. LDAP-инъекция может привести к произвольному чтению/записи данных из каталога сервера и, как следствие, к повышению привилегий учетных записей пользователей во всей сети. - XPATH-инъекции
Влияет на запрос к XML-документу. Может привести к произвольному раскрытию данных любой части документа. - Инъекция в командах ОС
Может привести к выполнению произвольной команды в ОС.
Общим во всех этих случаях является то, что конечный пользователь (или другое устройство) имеет возможность ввести некоторые данные в систему, которые изменяют семантику обрабатывающих их инструкций.
2. Логические ошибки в приложении
Многие исследователи в области компьютерной безопасности считают логику приложения менее интересной, по сравнению с изменением семантики инструкций. Однако, заботясь об устранении уязвимостей, которые могут привести к взлому веб-сервера, про логические ошибки часто забывают.
Логику можно назвать дефектной, если данные принимаются вслепую, без валидации, пропускаются какие-то шаги или при переходе к новому шагу алгоритма не проверяется результат предыдущего шага.
Логические ошибки приложения лучше всего проиллюстрировать на примере.
Допустим, вы разрабатываете e-Commerce сайт, использующий Paypal для процессинга платежей по кредитным картам. Для человека, не владеющего достаточным пониманием работы интеграции типа checkout, процесс выглядит так:
- Пользователь добавляет товары в свою корзину, выбирает способ оплаты Paypal и переходит на сайт платежной системы.
- Происходит некая магия.
- Пользователь нажимает на ссылку (не уникальную для него), при переходе по которой содержимое корзины помечается как оплаченное, и видит страницу успешного статуса оплаты.
Существует несколько возможных логических ошибок, которые могут здесь проявиться при условии, что вы явно не работали над тем, чтобы их предотвратить. Пользователь может:
- Добавить в корзину другой товар в количестве -1 с этой же ценой, снизив итоговую стоимость до 0$.
- Пропустить процесс checkout, заранее зная специальную ссылку (шаг 3), и транзакция автоматически станет оплаченной.
- Добавить дешевый товар в корзину, нажать на ссылку Paypal, вернуться в магазин и добавить группу дорогих товаров в корзину. Затем перейти на ранее открытую вкладку Paypal и завершить свою покупку с меньшей суммой. При этом она помечается как полностью оплаченная.
Первая проблема устраняется простой проверкой входных данных. В двух других случаях правильной будет только API-интеграция типа “сервер-сервер”. Вместо того чтобы полагаться на то, что пользователь перейдет по предоставленной ему ссылке (что может поломаться даже в совершенном мире, где никто не действует по злому умыслу), ваш checkout провайдер скажет вашему серверу, какие товары были куплены и какая сумма была за них перечислена.
Все попытки обмана в моем примере могут быть обнаружены с помощью ручного контроля, который, к сожалению, не масштабируется на большие объемы транзакций. Да и зачем создавать лишнюю работу вашим клиентам. Одним из преимуществ программного обеспечения является возможность применения автоматизации. Однако ее надежность зависит от безопасности вашей системы.
3. Рабочая среда вашего приложения
Ваше приложение не существует в вакууме. Его успешное функционирование зависит от большого количества других компонентов:
- сторонних библиотек;
- ОС сервера;
- аппаратного обеспечения сервера;
- монитора виртуальных машин, если ваше приложение работает на виртуальной машине;
- электроснабжения дата-центра;
- наличия подключения внутренней сети к внешнему миру;
- ПО конечного пользователя (веб-браузера).
Уязвимости в любом из этих компонентов могут ослабить или откровенно свести на нет безопасность всего вашего приложения. В этом случае единственный способ защиты — вовремя обновлять ПО и никогда не использовать ПО, которое больше не поддерживается его разработчиками.
В качестве примера можно привести проблему ассиметрии ресурсов. Большинство веб-сайтов физически работают на одной машине и не смогут справиться с нагрузкой, если на них поступит одновременно большое количество запросов от тысяч машин. Это называется DDoS-атакой. Единственный эффективный способ борьбы с этим находится на сетевом уровне, а не на уровне приложения.
4. Уязвимость шифрования
Хотя многие уязвимости, связанные с шифрованием, могут быть следствием логических ошибок приложения или некорректной работы его компонентов, шифрование действительно заслуживает отдельного рассмотрения.
Например, сравнение двух строк без применения шифрования не является чем-то особенным. Но вы должны быть уверены, что при использовании шифрования эта операция займет одинаковое количество времени для разных вариантов строк. Иначе ваше приложение становится уязвимым к тайминг-атаке — разновидности брутфорс-атаки, использующей для оптимизации перебора наблюдение за временем выполнения криптографических алгоритмов. Когда разработчик сам пытается писать алгоритмы с использованием шифрования, это может привести к катастрофе.
Шифрование сложно само по себе, поэтому предоставьте его экспертам. Полезно поэкспериментировать с шифрованием для его изучения, но никогда не выкладывайте такой код на боевой сервер и тем более никому не рекомендуйте его использовать.
Разнообразие уязвимостей безопасности
Весьма возможно, что существуют проблемы безопасности, о которых я не знаю. Также вероятно, что есть вопросы безопасности, которые не вписываются ни в одну из категорий, изложенных выше. Или существуют уязвимости, которые можно отнести к нескольким категориям.
Я полагаю, что моя классификация уязвимостей, пытающаяся охватить всю картину целиком за счет укрупнения категорий и уменьшения их числа — это более простой и ясный способ получить целостное представление о безопасности приложения, нежели ожидать от разработчиков заучивания произвольного списка наиболее распространенных уязвимостей за определенный год.
Конечно, она далека от совершенства. В конце концов, ответы на вопросы, какие категории должны быть, где должны проходить границы и как много уровней она должна иметь, может дать только профессиональное сообщество.
Приглашаются все желающие принять участие в усовершенствовании этой модели.
Это первый шаг, и если вы будете соблюдать следующие правила, вы будете готовы двигаться вперед, находясь в хорошей форме:
- Защитите инструкции от искажения данными, которые они обрабатывают.
- Добейтесь полного понимания и отсутствия сомнений по поводу логики вашего приложения.
- Поддерживайте в актуальном состоянии ПО системы, в котором работает ваше приложение, и не полагайтесь на ПО, которое больше не поддерживается.
- Забудьте про самостоятельную реализацию любых алгоритмов шифрования.
Автор: PavelOborin