En

JazzTeam Software Development Company

Agile Java Development

Рекомендации по безопасной разработке на платформе Java

Введение

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

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

Валидация входных данных

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

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

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

Удаление важной информации из exception messages

Внутренние исключения следует “отлавливать” и очищать от важной информации. Объекты исключений могут передавать конфиденциальную информацию. Например, если метод вызывает конструктор java.io.FileInputStream для чтения базового файла конфигурации, а этот файл отсутствует, вызывается исключение java.io.FileNotFoundException, содержащее путь к файлу. Распространение этого исключения обратно на вызывающего, предоставляет макет файловой системы. Многие формы атаки требуют знания или угадывания местоположений файлов.

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

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

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

Предотвращение попадания конфиденциальной информации в лог-файлы

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

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

Будьте осторожны при использовании динамических SQL-запросов

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

Для параметризованных SQL-запросов с использованием Java Database Connectivity (JDBC) используйте java.sql.PreparedStatement или java.sql.CallableStatement вместо java.sql.Statement. В общем, лучше использовать хорошо написанную библиотеку более высокого уровня, чтобы изолировать код приложения от SQL. При использовании такой библиотеки нет необходимости ограничивать символы, такие как quote ('). Если текст, предназначенный для XML / HTML, обрабатывается правильно во время вывода, тогда нет необходимости запрещать символы, такие как “меньше” (<), во входах в SQL.

Пример правильного использования PreparedStatement:

String sql = "SELECT * FROM User WHERE userId = ?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1, userId);
ResultSet queryResult = preparedStatement.executeQuery();

Понимание как суперкласс может повлиять на поведение подкласса

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

Ограничение расширяемости классов и методов

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

// Unsubclassable class with composed behavior.
     public final class SensitiveClass {

          private final Behavior behavior;

          // Hide constructor.
          private SensitiveClass(Behavior behavior) {
               this.behavior = behavior;
          }

          // Guarded construction.
          public static SensitiveClass newSensitiveClass(
               Behavior behavior
          ) {

               // ... validate any arguments ...

               // ... perform security checks ...

               return new SensitiveClass(behavior);
          }
}

Определение оберток над native-методами

Java-код подвергается проверкам времени выполнения. К ошибкам времени выполнения относятся: NullPointerException, IndexOutOfBoundsException, FileNotFoundException и т.д.. Native-код проверке не подвергается. В то время как чистый Java-код эффективно защищен от традиционных переполнений буфера, native-методы не защищены. Во избежаний проблем, описанных выше, и проблем безопасности - не вызывайте native-метод напрямую, а оберните его в Java-метод. Native-метод объявите как private и раскрывайте функциональность с помощью общедоступного метода обертки на основе Java. Обертка может безопасно выполнять любую необходимую валидацию перед вызовом собственного метода.

Предпочтительность неизменяемых типов для значений

Если метод возвращает ссылку на внутренний, изменяемый объект, вызывающий код может изменить внутреннее состояние экземпляра. Если в изменении поля нет необходимости, хорошей практикой является возвращение копии нужного поля. Для этого следует вызвать конструктор копирования для указанного поля или метод .clone().

Заключение

В данной статье приведены лишь некоторые рекомендации/практики по безопасному проектированию ПО:

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

, , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *