Как я могу защитить имя пользователя и пароль MySQL от декомпиляции? - PullRequest
78 голосов
/ 14 января 2009

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

Ответы [ 6 ]

112 голосов
/ 14 января 2009

Никогда не вводите пароли в свой код. Это было недавно упомянуто в Топ 25 самых опасных ошибок программирования :

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

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

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

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

import java.util.prefs.Preferences;

public class DemoApplication {
  Preferences preferences = 
      Preferences.userNodeForPackage(DemoApplication.class);

  public void setCredentials(String username, String password) {
    preferences.put("db_username", username);
    preferences.put("db_password", password);
  }

  public String getUsername() {
    return preferences.get("db_username", null);
  }

  public String getPassword() {
    return preferences.get("db_password", null);
  }

  // your code here
}

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

Важное примечание: Файлы настроек - это просто текстовые XML-файлы. Убедитесь, что вы предпринимаете соответствующие шаги, чтобы неавторизованные пользователи не могли просматривать необработанные файлы (разрешения UNIX, разрешения Windows и т. Д.). В Linux, по крайней мере, это не проблема, потому что вызов Preferences.userNodeForPackage создаст файл XML в домашнем каталоге текущего пользователя, который в любом случае не будет читаться другими пользователями. В Windows ситуация может быть иной.

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

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

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

Второй случай: попытка скрыть учетные данные пользователя

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

Правильный случай: использование многоуровневой архитектуры

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

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

Основной порядок операций:

  1. Клиент аутентифицируется на уровне бизнес-логики, используя личное имя пользователя / пароль. Имя пользователя и пароль известны пользователю и никак не связаны с учетными данными для входа в базу данных.
  2. Если аутентификация прошла успешно, клиент отправляет запрос на уровень бизнес-логики, запрашивая некоторую информацию из базы данных. Например, инвентарь продуктов. Обратите внимание, что запрос клиента не является запросом SQL; это удаленный вызов процедуры, такой как getInventoryList.
  3. Уровень бизнес-логики подключается к базе данных и получает запрошенную информацию. Уровень бизнес-логики отвечает за формирование безопасного SQL-запроса на основе запроса пользователя. Все параметры в запросе SQL должны быть обработаны для предотвращения атак внедрения SQL.
  4. Уровень бизнес-логики отправляет список инвентаризации обратно клиентскому приложению.
  5. Клиент отображает список инвентаря пользователю.

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

15 голосов
/ 14 января 2009

Поместите пароль в файл, который будет читать приложение. НИКОГДА не вставляйте пароли в исходный файл. Период.

Ruby имеет малоизвестный модуль DBI :: DBRC для такого использования. Я не сомневаюсь, что у Java есть эквивалент. Во всяком случае, написать не сложно.

3 голосов
/ 14 января 2009

Вы пишете веб-приложение? Если это так, используйте JNDI для внешней настройки приложения. Обзор доступен здесь :

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

2 голосов
/ 19 октября 2017

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

Лучший способ - нигде не хранить пароли. Это достигается использованием хеш-функций для генерации и хранения хешей паролей:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

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

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

Теперь возникает вопрос: как лучше всего хранить пароли? Я бы посчитал this (решение службы аутентификации и управления пользователями Stormpath) чертовски идеальным решением:

  1. Ваш пользователь вводит учетные данные, и это проверяется на соответствие хэш пароля
  2. Сгенерированы и сохранены хэши паролей, а не пароли
  3. Хеши выполняются несколько раз
  4. Хеши генерируются с использованием случайно сгенерированной соли
  5. Хеши шифруются закрытым ключом
  6. Закрытый ключ хранится в физически отличном от хеша месте
  7. Закрытые ключи обновляются по времени.
  8. Зашифрованные хэши делятся на куски
  9. Эти куски хранятся в физически отдельных местах

Очевидно, что вы не Google или банк, так что это решение для вас излишним. Но тогда возникает вопрос: сколько безопасности требует ваш проект, сколько у вас времени и денег?

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

Например, предположим, что шаг 1 не является приемлемым решением для вашего проекта. Вы не хотите, чтобы пользователи вводили пароль каждый раз, или вам даже не нужно / не нужно, чтобы пользователи знали пароль. Тем не менее, у вас есть конфиденциальная информация, и вы хотите защитить ее. У вас простое приложение, нет сервера для хранения ваших файлов или это слишком хлопотно для вашего проекта. Ваше приложение работает в среде, где невозможно безопасно хранить файлы. Это один из худших случаев, но все же с некоторыми дополнительными мерами безопасности у вас может быть гораздо более безопасное решение. Например, вы можете хранить конфиденциальную информацию в файле и шифровать файл. Вы можете жестко закодировать закрытый ключ шифрования в коде. Вы можете запутать код, так что вам будет немного сложнее его взломать. Для этого существует множество библиотек, см. эту ссылку . (Я хочу еще раз предупредить вас, что это не на 100% безопасно. Умный хакер с необходимыми знаниями и инструментами может взломать это. Но исходя из ваших требований и потребностей, это может быть достаточно хорошим решением для вас).

1 голос
/ 08 октября 2014

Этот вопрос показывает, как хранить пароли и другие данные в зашифрованном файле: 256-битное Java AES-шифрование на основе паролей

0 голосов
/ 14 января 2009

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

...