Как я могу защитить имя пользователя и пароль MySQL от декомпиляции?

87

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

Якоб Косороба
источник
Надеюсь, вы не против, я изменил теги на ваш вопрос. Я удалил «имя пользователя» и «пароль» и добавил «обратное проектирование» и «декомпиляцию». Я думаю, что они более наглядны, чем оригиналы. Кстати, отличный вопрос по основам!
Уильям Брендель
6
Обратите внимание, что тот факт, что вы используете Java, здесь не имеет значения. Наличие жестко запрограммированных паролей на любом языке почти одинаково проблематично ("strings thebinary" также показывает строковые константы в программах на C).
Joachim Sauer
@saua: Верно, но, возможно, кто-то опубликует пример кода о том, как разделять имена пользователей и пароли в Java. Я мог бы даже сделать это сам, если бы у меня было время.
Уильям Брендель
1
Я заметил, что многие ответы думают, что вы пытаетесь скрыть имя пользователя / пароли от неавторизованного пользователя, пока пользователь, запускающий приложение, в порядке. Я считаю, что вы хотите скрыть пароль от ВСЕХ . Пожалуйста, поясните это в вопросе.
pek
1
Например, предположим, что учетные данные используются для подключения к серверу, на который никто, кроме приложения, не может войти.
pek

Ответы:

123

Никогда не вставляйте пароли в свой код жестко. Это недавно было внесено в список 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.userNodeForPackageXML-файл в домашнем каталоге текущего пользователя, который в любом случае не читается другими пользователями. В Windows ситуация может быть иной.

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

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

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

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

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

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

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

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

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

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

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

Уильям Брендель
источник
4
Как именно это мешает кому-либо получить имя пользователя / пароль? Разве ты не можешь тогда просто прочитать это из файла?
Джо Филлипс,
Как я сказал в своем ответе, если ваши права доступа к файлам установлены правильно, только пользователь, запускающий программу, имеет доступ для чтения к этому файлу настроек. В средах UNIX это делается автоматически. Windows может потребовать дополнительных действий (я действительно не уверен, так как я мало пользуюсь Windows).
Уильям Брендель
Я думаю, что идея в том, что пользователь, запускающий приложение, не тот, от кого вы пытаетесь от него избавиться. Если это так, вам придется его зашифровать.
Майкл Харен,
Да, Майкл прав. По сути, идея заключается в том, что вы уже знаете имя пользователя / пароль, поэтому нет необходимости скрывать его от себя. Однако он будет скрыт от других пользователей через права доступа к файлам.
Уильям Брендель,
7
Если вы развертываете (пример) приложение для редактирования БД для пользователя и не хотите, чтобы он знал имя пользователя и пароль базы данных, значит, вы неправильно спроектировали решение, и клиентское программное обеспечение должно взаимодействовать с сервером (через например, веб-сервис), который выполняет работу с БД.
JeeBee
15

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

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

Кельтия
источник
7
Хотя это немного упрощает изменение паролей позже, это не решает основную проблему безопасности.
Брайан Ноблауч
Да. См. Также ответ Уильяма Бренделя.
Keltia
1
Метод, на который указали мы с Келтией, является общепринятым способом решения этой проблемы. Разделение ваших учетных данных для входа в систему от вашего скомпилированного кода - один из самых простых способов обеспечения безопасности программного обеспечения. Помещение учетных данных для входа в отдельный файл - эффективный способ добиться этого.
Уильям Брендель,
Более того, тот факт, что информация о вашей конфигурации находится в виде простого текстового файла, должен быть отменен ограничениями операционной системы. Например, в UNIX файл с открытым текстом должен принадлежать пользователю, запускающему программу, и иметь права доступа 0600, поэтому только владелец может его прочитать.
Уильям Брендель,
4
Итак, файл может быть прочитан только пользователем, запустившим программу. Отлично. Это ничего не решает. :-) Я, пользователь, от которого мы пытаемся сохранить пароль в секрете, могу прочитать его так же легко, как и приложение ...
Брайан Кноблауч,
3

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

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

Тим Хоуленд
источник
предоставленная ссылка плохая
Джеймс Оравек
2

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

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

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

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

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

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

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

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

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

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

Канер
источник
1

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

Аарон Дигулла
источник
1
Где-то в исходном коде все еще нужно расшифровать зашифрованный пароль для создания соединения, этот считающийся пароль все еще существует.
Bear0x3f
0

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

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