Уязвимости в коде. Как отличить опасную брешь от незначительной ошибки?

Уязвимости в коде. Как отличить опасную брешь от незначительной ошибки?
Как обычно выглядит проверка кода приложений на уязвимости? Специалист по безопасности инициирует процедуру, код сканируется, в приложении обнаруживаются тысячи уязвимостей. Все — и безопасник, и разработчики — в шоке. Естественная реакция разработчика: «Да наверняка половина — это ложные срабатывания, а другая — некритичные уязвимости!»

Что касается ложных срабатываний, здесь все просто: можно взять и посмотреть непосредственно те места кода, где обнаружены уязвимости с подозрением на false positive. Действительно, какая-то их часть может оказаться ложными срабатываниями, (хотя явно не половина от общего числа).

А вот о том, что критично, а что нет, хотелось бы поговорить более предметно. Если вы понимаете, почему сейчас уже нельзя использовать SHA-1 и зачем экранировать «;», возможно, эта статья не откроет вам чего-то нового. Но если по итогам сканирования от найденных уязвимостей рябит в глазах, добро пожаловать под кат – расскажем, какие «дыры» чаще всего встречаются в мобильных и веб-приложениях, как они работают, как их исправить, а главное — как понять, что перед вами — опасная брешь или незначительная ошибка в коде.



Внедрение


Ну ооочень распространенный тип уязвимости. Куда только не внедряются: в запросы SQL, LDAP, XML, XPath, XSLT, Xquery… Все эти внедрения отличает использование недоверенных данных, благодаря которому злоумышленник получает доступ к информации или изменяет поведение приложения. Например, с помощью пользовательского ввода, который недостаточно валидируется.

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

Внедрение в SQL-запрос. Недоверенные данные попадают в SQL-запрос к базе данных.



Если в запросе к базе данных не реализована корректная проверка подлинности вводимых данных, злоумышленник может испортить SQL-запрос:

  • передать в него вредоносный код;
  • добавить символ «--» или «;» и оборвать правильную SQL-команду: всё, что после «--», интерпретируется как комментарий, а символ «;» обозначает конец команды;
  • угадать пароль, последовательно выполнив серию SQL-запросов.

Как защищаться? Приведём несколько рекомендаций от OWASP :

  • Используйте API, который предоставляет параметризованный интерфейс, или инструменты объектно-реляционного сопоставления (ORM).
  • Реализуйте механизм валидации для данных, введённых пользователем. Используйте «белый список» проверки на стороне сервера.
  • Экранируйте специальные символы («;», «--», «/*», «*/», «'»; точный список зависит от базы данных).
  • Для проверки вводимых пользователем данных используйте хранимые процедуры (stored procedures) вместе с механизмом фильтрации их параметров.

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



Если злоумышленник может записывать данные в XML-документ, то он может изменять и его семантику. В таком случае самый безобидный сценарий позволит внедрить в документ лишние теги, в результате чего XML-парсер завершит работу с ошибкой. Но можно столкнуться и с инцидентом посерьезнее: например, с подменой аутентификационных данных в базе клиентов или ценой в базе товаров магазина. Внедрение в XML также может привести и к межсайтовому скриптингу (XSS) — внедрению вредоносного кода, который выполнится в браузере пользователя при открытии страницы.

Что можем посоветовать?

  • Не создавайте тегов и атрибутов, имена которых получены на основе данных из недоверенного источника (например, введённых пользователем).
  • Кодируйте (XML entity encode) данные, введённые пользователем, прежде чем записывать их в XML-документ.

Внедрение в XQuery представляет собой разновидность классической SQL-инъекции, но атака в этом случае будет направлена на XML-базу данных, а недоверенные данные попадут в XQuery-выражение.

В примере ниже приложение создаёт и выполняет XQuery-выражение на основе параметров username и password из HTTP-запроса (недоверенный источник):

XQDataSource xqs = new XQDataSource(); XQConnection conn = xqs.getConnection(); String query = "for $user in doc(users.xml)//user[username='" + request.getParameter("username") + "'and pass='" + request.getParameter("password") + "'] return $user"; XQPreparedExpression xqpe = conn.prepareExpression(query); XQResultSequence rs = xqpe.executeQuery();

В случае корректных данных запрос вернёт информацию о пользователе с соответствующими именем и паролем:

for $user in doc(users.xml)//user[username='test_user' and pass='pass123'] return $user 

Если злоумышленник задаст в качестве параметра строку, содержащую специальные символы (например, admin' or 1=1 or ''='), семантика запроса изменится:

//user[username='admin']

Полученный запрос вернёт данные обо всех пользователях.

Безопасный вариант (использует prepared statements):

XQDataSource xqs = new XQDataSource(); XQConnection conn = xqs.getConnection(); String query = "declare variable $username as xs:string external; declare variable $password as xs:string external; for $user in doc(users.xml)//user[username='$username' and pass='$password'] return $user"; XQPreparedExpression xqpe = conn.prepareExpression(query); xqpe.bindString(new QName("username"), request.getParameter("username"), null); xqpe.bindString(new QName("password"), request.getParameter("password"), null); XQResultSequence rs = xqpe.executeQuery();

Внедрение в XSLT (язык преобразования XML-документов) возможно, если приложение использует данные из недоверенного источника при работе с XSL.

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

Во-первых, XSS-атаку: внедрение в страницу, которую выдает веб-система, вредоносного кода и взаимодействие этого кода с сервером злоумышленника. Во-вторых, получение хакером доступа к системным ресурсам. В-третьих, выполнение произвольного кода. И на десерт – XXE-атаку (XML eXternal Entity — внедрение внешней сущности в XML).

Внедрение в команды LDAP (Lightweight Directory Access Protocol — «легкорасширяемый протокол доступа к каталогам») может привести к утрате конфиденциальности данных или их модификации. В данном случае недоверенные данные попадают в LDAP-запрос.

Внедрение вредоносной команды интерпретатора. Недоверенные данные попадают в команду интерпретатора. Злоумышленник может подобрать такой ввод, чтобы команда выполнилась успешно и ему стали доступны дополнительные полномочия в приложении.

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

String btype = request.getParameter("backuptype"); String cmd = new String("cmd.exe /K "c:utilrmanDB.bat "+btype+"&&c:utlcleanup.bat"") System.Runtime.getRuntime().exec(cmd);

Проблема здесь в том, что параметр backuptype не валидируется. Обычно Runtime.exec() не выполняет несколько команд, но в данном случае сначала запускается cmd.exe, чтобы выполнить несколько команд вызовом Runtime.exec(). Как только оболочка командной строки запущена, она может выполнить несколько команд, разделённых символами «&&». Если злоумышленник задаст в качестве параметра строку "&& del c:dbms*.*", приложение удалит указанную директорию.

Советы разработчикам:

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

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

Небезопасное включение внешнего файла в HTML. Уязвимости типа «file inclusion» возникают, когда пользователь вводит путь к подключаемому файлу. Дело в том, что современные скриптовые языки позволяют динамически подключать код из сторонних файлов, чтобы использовать его повторно. Этот механизм применяют для единого внешнего вида страниц или для разделения кода на небольшие модули. Однако таким включением может воспользоваться злоумышленник, подменив путь и подключив свой файл.

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

Закладки


Закладками именуют преднамеренно внесённые в код приложения части, с помощью которых при определённых условиях можно выполнить не заложенные в приложении действия. Рассмотрим самые распространенные виды закладок.

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



Разработчик приложения использует специальную учётную запись (возможно, с повышенными привилегиями) при отладке и оставляет соответствующие участки кода в финальной версии, сохранив за собой доступ к приложению. Злоумышленник может восстановить исходный код приложения, извлечь константные значения специальной учётной записи и получить доступ к приложению.
Хранить логины, пароли, ключи в исходном коде приложения категорически нельзя.
Скрытая функциональность (НДВ). Код скрытой функциональности запускается, когда срабатывает определенный триггер. В веб-приложениях триггером часто служит «невидимый» параметр запроса. Иногда дополнительно осуществляется проверка, с какого IP пришел запрос с триггером, чтобы активировать закладку мог только её автор. Такие проверки служат сигналом к возможным закладкам.

Недокументированная сетевая активность. К такому виду активности относятся: соединение со сторонними ресурсами в фоновом режиме, прослушивание недокументированных портов, передача информации по протоколами SMTP, HTTP, UDP, ICMP.
Если вы обнаружили в коде подозрительное соединение с адресом, который не входит в список заведомо безопасных, настоятельно рекомендуем вам его удалить.
Изменение параметров безопасности. Приложение содержит код, который изменяет значение переменной, хранящей успешность аутентификации. Распространённая ошибка — использование присваивания (=) вместо сравнения (==). В методах, связанных с аутентификацией, она особенно опасна, поскольку может быть частью бэкдора:

if (isAuthenticated = true) {     someDangerousAction(); } 

Временной триггер (timebomb). Закладка, которая срабатывает в определенный момент времени. Приложение сравнивает текущую дату с конкретными годом, месяцем и днём: 1 января 2021 года всех ждёт сюрприз:

Date now = java.util.Date();    // current time if ((now.getYear() == 2021) && (now.getMonth() == 1) && (now.getDate() == 1)) {     activateNewYearBackdoor(); }

А может быть и нет… На практике при поиске временных триггеров часто происходят ложные срабатывания. Например, если API времени используют и по назначению: запись в лог, вычисление времени выполнения, временные метки для ответов сервера на HTTP-запросы.

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

Мёртвый код. Куски внедренного кода, которые ничего полезного не делают. Сам по себе мёртвый код не опасен, однако он может быть частью закладки, которую распределили по нескольким файлам. Или же триггер срабатывания закладки планируется внедрить позже. В любом случае, мёртвый код должен вызывать подозрения.

Отсутствие шифрования и использование слабых алгоритмов шифрования


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

В примере показана инициализация шифрования по устаревшему алгоритму DES:

Cipher cipher = Cipher.getInstance("DES");

Примеры уязвимых алгоритмов шифрования: RC2, RC4, DES. Безопасный вариант:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

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

Наши рекомендации разработчикам: обязательно используйте шифрование с учётом требований безопасности.

Применение незащищённого протокола HTTP вместо HTTPS чревато атакой типа Man in the middle.

Безопасный протокол HTTPS основан на HTTP, но также поддерживает шифрование через криптографические протоколы SSL/TLS. HTTPS шифрует все передаваемые по нему данные, в частности, страницы ввода логина и пароля или данные банковских карт пользователя, защищая их от несанкционированного доступа и изменения. В отличие от HTTP, который не защищает передаваемые данные. В результате злоумышленник может подменить информационный сайт по HTTP и заставить пользователя ввести данные на поддельной странице (фишинговая атака).

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

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

Наш совет: генерируйте ключи криптостойкими генераторами псевдослучайных чисел (ГПСЧ) и храните их с помощью специальных модулей.

Небезопасный алгоритм дополнения при шифровании. Если алгоритм шифрования RSA используется без OAEP-дополнения, зашифрованные данные становятся уязвимыми .

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

Это пример инициализации RSA-шифрования без дополнения:

rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding"); 

Безопасный вариант:

rsa = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding");

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

Криптоанализ не стоит на месте, постоянно возникают новые алгоритмы атак, компьютеры обретают бОльшие мощности. Параметры шифрования, которые раньше считались безопасными, устаревают и уже не рекомендуются к использованию. Так, RSA с длиной ключа 1024 бит перестал считаться безопасным в 2010–2015 годах.

Слабый алгоритм хеширования. По описанным в предыдущем пункте причинам хеш-функции MD2, MD5, SHA1 являются небезопасными. Чтобы найти коллизии для функций MD2 и MD5, существенных ресурсов не требуется.

Для SHA1 есть примеры двух разных файлов с одинаковыми хешами. Алгоритм взлома предложили сотрудники Google и Центра математики и информатики в Амстердаме.



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

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

Несколько интересных цифр: с помощью PBKDF2 скорость перебора ключей сократили до 70 штук в секунду для Intel Core2 и около 1 тысячи на ПЛИС Virtex-4 FX60. Для сравнения, классические функции хеширования пароля LANMAN имеют скорость перебора около сотен миллионов вариантов в секунду.

Слабый алгоритм шифрования. Как и в случае алгоритмов хеширования, безопасность алгоритма шифрования определяется временем и ресурсами, которые придется потратить на его дешифровку. Уязвимыми алгоритмами считаются RC2, RC4, DES. Последний из-за небольшой длины ключа (56 бит) можно взломать методом полного перебора.

Слабый генератор псевдослучайных чисел (ГПСЧ) порождает предсказуемые последовательности. Хакер может обойти аутентификацию и захватить сессию пользователя.

Углубимся немного в природу ГПСЧ. Они порождают цепочки чисел на основе начального значения параметра seed. Есть два типа ГПСЧ – статистические и криптографические.

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

Результат работы криптографических ГПСЧ, наоборот, невозможно предугадать, если значение параметра seed получено из источника с высокой энтропией. Значение текущего времени обладает небольшой энтропией и также небезопасно в качестве seed. В Java ГПСЧ из классов java.util.Random и java.lang.Math порождают предсказуемые последовательности и не должны использоваться в целях обеспечения информационной безопасности.

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

Работа многих криптографических алгоритмов основана на использовании устойчивого к криптоанализу ГПСЧ. Некоторые алгоритмы могут принимать в качестве дополнительного аргумента значение seed и для каждого значения этого параметра порождать предсказуемую последовательность. В таком случае безопасность системы основана на предположении о том, что значения seed будут непредсказуемы.

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

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

Манипуляции с логами


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

Подделка файла лога происходит, когда приложение записывает недоверенные данные в журнал событий (лог). Хакер может подделать записи лога или внедрить в них вредоносный код.

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

В этом примере web-приложение пытается считать целочисленное значение из параметра запроса. Если введённое значение не удалось конвертировать в целое число, приложение записывает в лог это значение вместе с сообщением об ошибке:

String val = request.getParameter("val"); try {     int value = Integer.parseInt(val); } catch (NumberFormatException nfe) {     log.info("Failed to parse val = " + val); }

Злоумышленник может добавить в лог произвольную запись, например, строка twenty-one%0a%0aINFO:+User+logged+out%3dbadguy отразится в логе так:

INFO: Failed to parse val=twenty-one INFO: User logged out=badguy 

Аналогичным образом в лог можно внедрить произвольные записи.

Безопасный вариант (использует NumberFormatException):

public static final String NFE = "Failed to parse val. The input is required to be an integer value."  String val = request.getParameter("val"); try {     int value = Integer.parseInt(val); } catch (NumberFormatException nfe) {     log.info(NFE); }

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

Небезопасная работа с куки


Уязвимости, связанные со сбором пользовательских куки, весьма разнообразны.

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

Если в приложение внедряется вредоносный код (межсайтовый скриптинг), то злоумышленник сможет изменять куки пользователя.

Поскольку куки задаются в заголовке HTTP-ответа, отсутствие подтверждения данных, включаемых в куки, может привести к атаке «расщепление ответа». «Расщеплением ответа» (HTTP response splitting) называется атака, при которой хакер посылает такой HTTP-запрос, ответ на который примется жертвой сразу за два HTTP-ответа (вместо правильного одного).
Если злоумышленник задаст в качестве параметра author строку вида Hacker r
HTTP/1.1 200 OKr
...
, ответ будет расщеплён на два следующим образом:

HTTP/1.1 200 OK ... Set-Cookie: author=Hacker  HTTP/1.1 200 OK ...

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

Куки без HttpOnly. Приложение создаёт куки без флага httpOnly. Если httpOnly включается в заголовок ответа http, злоумышленник не сможет заполучить куки с помощью JavaScript-кода. И если пользователь откроет страницу с уязвимостью типа межсайтовый скриптинг (XSS), браузер не раскроет куки третьим лицам. Если же флаг httpOnly не установлен, то куки (обычно, сессионные) можно украсть с помощью скрипта.

Пример создания куки без флага httpOnly:

Cookie cookie = new Cookie("emailCookie", email); response.addCookie(cookie);

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

N.B. Согласно международной классификации OWASP , уязвимости типа «утечка конфиденциальных данных» занимают третье место по уровню критичности угроз безопасности веб-приложений.

Куки для слишком общего домена. Если домен для куки слишком общий (например, .example.com), уязвимость в одном приложении делает уязвимыми и другие приложения в том же домене.

В следующем примере безопасное web-приложение, установленное по адресу http://secure.example.com, устанавливает куки со значением домена .example.com:

Cookie cookie = new Cookie("sessionID", sessionID); cookie.setDomain(".example.com");

Если по адресу http://insecure.example.com установлено приложение, содержащее, например, XSS, то куки авторизованного пользователя безопасного приложения, перешедшего по адресу http://insecure.example.com, могут быть скомпрометированы.

Злоумышленник также может осуществить атаку «отравление куки»: куки с общим доменом, созданные http://insecure.example.com, перезапишут куки http://secure.example.com.

Безопасный вариант:

Cookie cookie = new Cookie("sessionID", sessionID); cookie.setDomain("secure.example.com");

Куки со слишком общим параметром path. Если путь в куки указан неточно (например, /), возникает такая же проблемы, как и с общим доменом: уязвимость в одном приложении подвергает опасности и другие приложения в том же домене.

В следующем примере приложение, установленное по адресу http://pages.example.com/forum, устанавливает куки с путём /:

Cookie cookie = new Cookie("sessionID", sessionID); cookie.setPath("/"); 

Тогда вредоносное приложение, установленное по адресу http://pages.example.com/evil, может скомпрометировать куки пользователя. Злоумышленник также может осуществить атаку «отравление куки»: куки с общим путём, созданные /evil, перезапишут куки /forum.

Безопасный вариант:

Cookie cookie = new Cookie("sessionID", sessionID); cookie.setPath("/forum"); 

Куки не по SSL. Приложение создаёт куки, не устанавливая флаг secure равным true. Такие куки можно передавать в незашифрованном виде по HTTP. Сразу же вспоминается уязвимость «Использование незащищённого протокола HTTP».

В следующем примере приложение создаёт куки без флага secure:

Cookie cookie = new Cookie("emailCookie", email); response.addCookie(cookie);

Если приложение использует как HTTPS, так и HTTP, то при отсутствии флага secure куки, созданные в рамках HTTPS-запроса, будут передаваться в незашифрованном виде при последующих HTTP-запросах, что может привести к компрометации приложения. Это особенно опасно, если куки содержит ценные данные, в частности, идентификатор сессии.

Безопасный вариант:

Cookie cookie = new Cookie("emailCookie", email); cookie.setSecure(true); response.addCookie(cookie);

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

По умолчанию используются непостоянные (сессионные) куки, которые не сохраняются на диск и удаляются после закрытия браузера. Однако разработчик веб-приложения может указать срок хранения куки – в этом случае они будут записаны на диск и сохранены между перезапусками браузера и перезагрузками компьютера. Так мы даем злоумышленнику продолжительное время для разработки плана атаки.

Рекомендации разработчикам: убедитесь, что приложение не создаёт куки с длительным сроком хранения:

Cookie cookie = new Cookie("longCookie", cookie); cookie.setMaxAge(5*365*24*3600); // 5 лет!

Указывайте разумный максимальный срок, следуя рекомендациям OWASP .

Утечка информации


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

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

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

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



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

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

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

Послесловие


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

Автор: Елизавета Харламова, руководитель отдела аналитики Solar appScreener
Alt text

Подписывайтесь на каналы "SecurityLab" в TelegramTelegram и TwitterTwitter, чтобы первыми узнавать о новостях и эксклюзивных материалах по информационной безопасности.