Основы безопасного программирования

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

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

1. Защита конфиденциальной информации в журналах

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

Пример: toString().

Как показано на рисунке, мы используем toString() для предоставления информации о BankInfo, которая может быть использована при записи в журнал. Но во время процесса записи передаются также конфиденциальные сведения.

  • Пароли, финансовая информация, системные и персональные данные  —  наиболее распространенные “мишени” в журналах.
  • Во время протоколирования следует использовать не автоматически генерируемые методы IDE, а специальный метод toString(), который не предусматривает включение конфиденциальной информации. Когда речь заходит о безопасности, toString()  —  один из самых надежных методов ее обеспечения.
  • Избыточное протоколирование также может способствовать возникновению уязвимостей в безопасности и оказывать давление на статистику системы.
  • Если требуется передача конфиденциальной информации, зашифруйте ее во время записи в журнал. Однако помните: избыточное шифрование замедляет скорость работы системы.
  • Избегайте слишком большого количества информации в сообщениях об исключениях или при записи трассировки стека в журнал. Данные об ошибках/исключениях  —  хороший источник информации. Используйте обычные сообщения об ошибках. Так недобросовестные пользователи не смогут узнать о том, как работает система.

2. Использование проверенных библиотек

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

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

Вот некоторые распространенные инструменты, используемые разработчиками на данный момент:

3. Инъекционные атаки

Наиболее распространенной и самой известной из инъекционных атак является SQL Injection. Базовая SQL инъекционная атака использует входные данные пользователя. Веб-приложения принимают входные данные через формы, которые передают их в базу данных для обработки. Если веб-приложение принимает эти данные без их “санитарной” обработки, злоумышленник может внедрить SQL-операторы через поля формы и удалить, скопировать или изменить содержимое базы данных (cookies и HTTP-заголовки также подходят для этих целей).

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

При добавлении or ‘a’ = ‘a’ в запрос вместе с входными данными человеку становятся доступны все транзакции.

Для сброса всех транзакций таблицы достаточно добавить к запросу вместе с входными данными ; Drop Table transactions.

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

Избежать таких атак можно путем использования параметризованных запросов вместо конкатенации по отношению к строковым запросам.

Параметризованные запросы могут использоваться в любой ситуации, когда ненадежные входные данные появляются в качестве данных в запросе, включая предложение WHERE и значения в операторе INSERT или UPDATE. Чтобы параметризованный запрос сработал для предотвращения SQL-инъекций, строка, используемая в запросе, всегда должна быть жестко закодированной константой и никогда не должна содержать переменных данных любого происхождения.

4. Избегайте сериализации

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

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

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

Существует несколько советов при использовании процессов сериализации/десериализации, о которых вам следует знать.

  • Не применяйте сериализацию в отношении полей, содержащих конфиденциальные данные: это раскрывает их значения для любой стороны, имеющей доступ к потоку сериализации.
  • Пишите методы сериализации со спецификой по классам.
  • Шифруйте поток байтов.
  • Не принимайте сериализованные объекты из ненадежных источников.
  • Сведите к минимуму использование сериализации.

5. Изменяемость

Хотя такая вещь как изменяемость кажется безобидной, она может стать причиной множества проблем с безопасностью. Например, вместо использования популярного пакета java.util.Date, являющегося примером изменяемого класса API, лучше взять на вооружение новый инструмент Java Date and Time API (java.time.*), который был сделан разработчиками неизменяемым.

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

  • Создание неизменяемых классов предотвращает появление в клиентском коде проблем, связанных с изменяемыми объектами. Неизменяемые классы не должны быть подклассами.
  • Скрытие конструкторов позволяет более гибко подходить к созданию и кэшированию инстансов. Это означает, что конструктор должен быть приватным, к нему нужно иметь стандартный доступ или он должен находиться в пакете, контролируемом свойством безопасности package.access.
  • Если метод возвращает ссылку на внутренний изменяемый объект, то клиентский код может вносить изменения во внутреннее состояние инстанса. Если не предполагается обмен состоянием, копируйте изменяемые объекты и возвращайте копию.
  • Не полагайтесь на тождественное равенство при переопределении входных ссылочных объектов. Переопределяемые методы могут вести себя вопреки ожиданиям. Например, если ожидается поведение тождественного равенства, Object.equals может быть переопределен, чтобы вернуть значение true для разных объектов.
  • Если состояние, которое является внутренним для класса, должно быть общедоступным и изменяемым, объявите приватное поле и обеспечьте доступ к нему с помощью публичных методов-оберток. Если состояние должно быть доступно только подклассам, объявите приватное поле и обеспечьте доступ к нему с помощью защищенных методов-оберток. С помощью методов-оберток можно проводить проверку входных данных перед установкой нового значения.

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

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Vishalgoel: Secure Coding Fundamentals

Предыдущая статьяПерегрузка функций в TypeScript
Следующая статьяНовый API форматировщика дат в Swift