Ответ 1
Никогда не записывайте пароли в свой код. Это было недавно поднято в Топ-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
после отображения диалога askign для имени пользователя и пароля. Когда вам нужно подключиться к базе данных, вы можете просто использовать методы getUsername
и getPassword
для извлечения сохраненных значений. Учетные данные для входа не будут жестко закодированы в ваши двоичные файлы, поэтому декомпиляция не будет представлять угрозу безопасности.
Важное примечание: Файлы настроек - это просто текстовые XML файлы. Убедитесь, что вы предпринимаете соответствующие шаги, чтобы предотвратить несанкционированный доступ к необработанным файлам (разрешения UNIX, разрешения Windows и т.д.). В Linux, по крайней мере, это не проблема, потому что вызов Preferences.userNodeForPackage
создаст XML файл в текущем домашнем каталоге пользователя, который в любом случае не читается другими пользователями. В Windows ситуация может быть иной.
Важные примечания: В комментариях к этому ответу и другим вопросам было много обсуждений относительно правильной архитектуры для этой ситуации. В исходном вопросе не упоминается контекст, в котором используется приложение, поэтому я расскажу о двух ситуациях, о которых я могу думать. Первое - это случай, когда пользователь, использующий программу, уже знает (и имеет право знать) учетные данные базы данных. Во-вторых, это тот случай, когда вы, разработчик, пытаетесь сохранить секретные данные базы данных от человека, использующего программу.
Первый случай: пользователь имеет право знать учетные данные для входа в базу данных
В этом случае решение, упомянутое выше, будет работать. Класс Java Preference
будет хранить имя пользователя и пароль в виде обычного текста, но файл настроек будет доступен только для авторизованного пользователя. Пользователь может просто открыть XML файл предпочтений и прочитать учетные данные для входа, но это не угроза безопасности, потому что пользователь знал учетные данные для начала.
Второй случай: попытка скрыть учетные данные пользователя от пользователя
Это более сложный случай: пользователь не должен знать учетные данные для входа, но все же нуждается в доступе к базе данных. В этом случае пользователь, запускающий приложение, имеет прямой доступ к базе данных, что означает, что программе необходимо заранее знать учетные данные для входа. Решение, упомянутое выше, не подходит для этого случая. Вы можете сохранить учетные данные для входа в базу данных в файле настроек, но пользователь сможет прочитать этот файл, так как он будет владельцем. На самом деле, действительно нет хорошего способа использовать этот случай безопасным способом.
Правильный пример: использование многоуровневой архитектуры
Правильный способ сделать это - иметь средний уровень между сервером базы данных и вашим клиентским приложением, который проверяет подлинность отдельных пользователей и позволяет выполнять ограниченный набор операций. Каждый пользователь будет иметь свои собственные учетные данные, но не для сервера базы данных. Учетные данные позволят получить доступ к среднему уровню (уровень бизнес-логики) и будут отличаться для каждого пользователя.
У каждого пользователя есть свое имя пользователя и пароль, которые могут храниться локально в файле настроек без какого-либо риска для безопасности. Это называется трехуровневой архитектурой(уровни - ваш сервер базы данных, сервер бизнес-логики и клиентское приложение). Это сложнее, но на самом деле это самый безопасный способ сделать это.
Основной порядок операций:
- Клиент аутентифицируется с использованием уровня бизнес-логики с использованием личного имени пользователя/пароля пользователя. Имя пользователя и пароль известны пользователю и никак не связаны с учетными данными входа в базу данных.
- Если аутентификация завершается успешно, клиент делает запрос на уровень бизнес-логики, запрашивая некоторую информацию из базы данных. Например, инвентаризация продуктов. Обратите внимание, что запрос клиента не является SQL-запросом; это удаленный вызов процедуры, например
getInventoryList
. - Уровень бизнес-логики соединяется с базой данных и получает запрошенную информацию. Уровень бизнес-логики отвечает за формирование безопасного SQL-запроса на основе пользовательского запроса. Любые параметры SQL-запроса должны быть дезинфицированы для предотвращения атак SQL-инъекций.
- Уровень бизнес-логики возвращает список инвентаря обратно в клиентское приложение.
- Клиент отображает список инвентаря для пользователя.
Обратите внимание, что во всем процессе клиентское приложение никогда не подключается напрямую к базе данных. Уровень бизнес-логики получает запрос от аутентифицированного пользователя, обрабатывает запрос клиента для списка инвентаря и только затем выполняет SQL-запрос.