Никогда не вводите пароли в свой код. Это было недавно упомянуто в Топ 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-файл настроек и прочитать учетные данные для входа, но это не является угрозой безопасности, поскольку пользователь знал учетные данные для начала.
Второй случай: попытка скрыть учетные данные пользователя
Это более сложный случай: пользователь не должен знать учетные данные для входа в систему, но ему все еще требуется доступ к базе данных. В этом случае пользователь, запускающий приложение, имеет прямой доступ к базе данных, а это значит, что программе необходимо заранее знать учетные данные для входа. Решение, которое я упомянул выше, не подходит для этого случая. Вы можете сохранить учетные данные для входа в базу данных в файле настроек, но пользователь сможет прочитать этот файл, поскольку они будут его владельцем. На самом деле, нет никакого хорошего способа использовать этот случай безопасным способом.
Правильный случай: использование многоуровневой архитектуры
Правильный способ сделать это - создать промежуточный уровень между сервером базы данных и клиентским приложением, который аутентифицирует отдельных пользователей и позволяет выполнять ограниченный набор операций. У каждого пользователя будут свои учетные данные для входа, но не для сервера базы данных. Учетные данные позволят получить доступ к среднему уровню (уровню бизнес-логики) и будут отличаться для каждого пользователя.
Каждый пользователь будет иметь свое собственное имя пользователя и пароль, которые могут храниться локально в файле настроек без какой-либо угрозы безопасности. Это называется трехуровневая архитектура (уровни - это сервер базы данных, сервер бизнес-логики и клиентское приложение). Это сложнее, но это действительно самый безопасный способ сделать подобные вещи.
Основной порядок операций:
- Клиент аутентифицируется на уровне бизнес-логики, используя личное имя пользователя / пароль. Имя пользователя и пароль известны пользователю и никак не связаны с учетными данными для входа в базу данных.
- Если аутентификация прошла успешно, клиент отправляет запрос на уровень бизнес-логики, запрашивая некоторую информацию из базы данных. Например, инвентарь продуктов. Обратите внимание, что запрос клиента не является запросом SQL; это удаленный вызов процедуры, такой как
getInventoryList
.
- Уровень бизнес-логики подключается к базе данных и получает запрошенную информацию. Уровень бизнес-логики отвечает за формирование безопасного SQL-запроса на основе запроса пользователя. Все параметры в запросе SQL должны быть обработаны для предотвращения атак внедрения SQL.
- Уровень бизнес-логики отправляет список инвентаризации обратно клиентскому приложению.
- Клиент отображает список инвентаря пользователю.
Обратите внимание, что во всем процессе клиентское приложение никогда не подключается напрямую к базе данных . Уровень бизнес-логики получает запрос от аутентифицированного пользователя, обрабатывает запрос клиента на инвентаризацию и только затем выполняет запрос SQL.