Проверка подлинности в спящем режиме без паролей, хранящихся в текстовом виде
Моя цель - безопасная аутентификация базы данных с помощью JDBC/Hibernate без сохранения паролей в текстовом формате. Обоснованы примеры кода. Я уже использую waffle для аутентификации пользователя, поэтому, если есть какой-то способ использовать учетные данные, полученные waffle от пользователя, и перенаправить их в БД, это будет хорошо.
Два вопроса:
- Каков рекомендуемый способ аутентификации нескольких хостов (клиент, веб-сервер и база данных - все разные машины) с tomcat/hibernate/ spring на веб-сервере, базе данных SQL и, очевидно, в браузере клиента?
- Я также соглашаюсь на способ использования одной учетной записи пользователя для аутентификации, если информация об учетной записи пользователя не хранится в обычном тексте в любом месте. Учетная запись пользователя будет иметь права на чтение и запись в БД.
Я нашел полезную информацию о подключении к SQL Server в этом потоке. Тем не менее, я ожидаю, что Tomcat будет работать под учетной записью по умолчанию, которая похожа на локальную систему или что-то в этом роде. Насколько я знаю, эта учетная запись не может использоваться для проверки подлинности Windows в базе данных.
Мое решение:
В конце концов я использовал подход, упомянутый в приведенном выше потоке. Вместо того, чтобы запускать службу Tomcat как Local System, она теперь работает как пользователь. У этого пользователя есть разрешение на доступ к базе данных. Мой конфигурационный файл спящего режима настроен следующим образом:
<property name="hibernate.connection.url">
jdbc:sqlserver://system:port;databaseName=myDb;integratedSecurity=true;
</property>
Тем, кто предоставил ответы
Я благодарен всем за помощь, и я попробую некоторые из техник, упомянутых в потоке. Моя проблема с некоторыми ответами заключается в том, что они требуют симметричного шифрования, требующего секретного ключа. Сохранение секретности ключа - это почти та же проблема, что и сохранение пароля в виде обычного текста.
Ответы
Ответ 1
Хорошо, давайте рассмотрим проблему. Вы хотите, чтобы информация об аутентификации была доступна, но не была закодирована в любом месте кода или в файловой системе. Что я хотел бы предложить:
- требуется, чтобы администратор приложения указывал информацию об аутентификации при запуске приложения либо через jmx, либо через веб-страницу, которая не требует подключения к базе данных.
- Добавить фильтр сервлетов, чтобы ограничить доступ до тех пор, пока не будет введена информация о проверке подлинности базы данных.
Это решение требует некоторой расширенной загрузки контекста spring, чтобы он ожидал, пока не будет указана информация аутентификации (через страницу входа).
Ответ 2
i недавно написал об этом в блоге:
вы можете сказать tomcat jdbcrealm использовать алгоритм дайджеста на пароле, например sha-256, и сохранить хеш, а не пароли открытого текста.
Предположим, что ваши пользовательские объекты выглядят следующим образом:
@Entity
@Table(name = "cr_users")
public class UserDetails{
@Id
@GeneratedValue
private long id;
private String name;
private String passwordHash;
@ManyToMany
private Set<Group> groups;
}
при создании нового пользователя через службу можно создать хэш пароля с помощью MessageDigest:
public UserDetails createNewUser(String username,String passwd,Set<Group> groups){
UserDetails u=new UserDetails();
u.setname(username);
u.setGroups(groups);
u.setPassword(createHash(passwd));
return u;
}
public String createHash(String data){
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(password.getBytes());
byte byteData[] = digest.digest();
//convert bytes to hex chars
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
так как SHA-256 всегда будет давать одно и то же значение hashval для того же входа, вы можете сказать tomcat JDBCRealm использовать этот алгоритм для проверки паролей.
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost:5432/mydb"
connectionName="myuser" connectionPassword="mypass"
userTable="tc_realm_users" userNameCol="username" userCredCol="passwordhash"
userRoleTable="tc_realm_groups" roleNameCol="groupname"
digest="sha-256"/>
проблема в том, что tomcat ожидает отдельный формат для следующего типа:
+----------------------+ +-------------------+
| tc_realm_users | | tc_realm_groups |
+----------------------+ +-------------------+
| username varchar | | username varchar |
| passwordhash varchar | | groupname varchar |
+----------------------+ +-------------------+
если ваша модель пользовательских данных подходит вам, вам повезло, но мои сгенерированные таблицы Hibernate выглядели следующим образом:
+----------------------+ +-------------------+ +--------------------+
| cr_users | | cr_groups | | cr_users_cr_groups |
+----------------------+ +-------------------+ +--------------------+
| id long | | id long | | cr_users_id long |
| name varchar | | name varchar | | groups_id long |
| passwordhash varchar | +-------------------+ +--------------------+
+----------------------+
поэтому я создал View, используя SQL, который имел ожидаемый формат и извлекал данные из моих пользовательских данных webapps:
create view tc_realm_groups as
select
cr_users.name as username,
groups.name as groupname
from cr_users
left join (
select
cr_users_cr_groups.cr_users_id,cr_groups.name
from cr_groups
left join
cr_users_cr_groups
on cr_users_cr_groups.groups_id=cr_groups.id
) as groups on groups.cr_users_id=id;
create view tc_realm_users as
select
name as username
from cr_users;
с этим tomcat смог аутентифицировать/авторизировать снова мои уже существующие пользовательские данные и написать данные в контексте, чтобы я мог использовать его в Джерси (JSR-311) ресурсы:
public Response getEvent(@Context SecurityContext sc,@PathParam("id") long id) {
log.debug("auth: " + sc.getAuthenticationScheme());
log.debug("user: " + sc.getUserPrincipal().getName()); // the username!
log.debug("admin-privileges: " + sc.isUserInRole("webapp-admin"));
return Response.ok("auth success").build();
}
существуют также некоторые другие реализации Realm:
- JDBCRealm
- DataSourceRealm
- JNDIRealm
- UserDatabaseRealm
- MemoryRealm
- JAASRealm
- CombinedRealm
- LockOutRealm
некоторые ссылки:
Ответ 3
Если я правильно понимаю, ваша среда - это веб-приложение с поддержкой hibernate, развернутое в tomcat.
Теперь в настоящее время вы должны настроить протокол передачи JDBC
i) либо в файле конфигурации hibernate (обычно файл hibernate.cfg.xml) в свойстве: -
hibernate.connection.password
ii) или в файле конфигурации tomcat: -
<Resource name="jdbc/myoracle" ......password="tiger".../>
Теперь вы хотите НЕ хранить ясный пароль в любом из вышеуказанных файлов.
В коде приложения вы должны: -
Line1: org.hibernate.cfs.Condiguration configuration=new Configraution().configure(<hibernate configuration path>);
then,Line2: configuration.buildSessionFactory().openSession() to create a hibernate session which has underlying JDBC connection.
1) Один из способов может быть в основном: -
Вы можете зашифровать свой пароль, используя любой java-security alogirthm, используя любой поставщик JCE. Вы храните зашифрованный пароль в любом из вышеуказанных конфигурационных файлов (спящий режим или tomcat в соответствии с вашей средой проекта).
а затем между Line1 и Line2, вы можете использовать логику дешифрования: -
Line1: org.hibernate.cfs.Condiguration configuration=new Configraution().configure(<hibernate configuration path>);
String encrpytedPassword=
configuration.getProperty("hibernate.connection.password"); \\will return encrypted password
//decryption logic to decypt the encrypted password:-
String decryptedPwd=decrypt(encrpytedPassword);
configuration.setProperty("hibernate.connection.password",decryptedPwd);
then,Line2: configuration.buildSessionFactory().openSession()
Вы можете сделать шифрование и дешифрование такими сложными, как вы хотите, например. шифрование пароля с обратной строкой-clear-password.
Вы можете использовать любой JCE API: - jasrypt, bouncy castle.
Вам должно понадобиться понимание java-криптографии. Пожалуйста, обратитесь к: -
http://download.oracle.com/javase/1.4.2/docs/guide/security/CryptoSpec.html
2) В случае, если вы беспокоитесь о том, что пароль передается в ядре в протоколе соединения JDBC, вы можете использовать поддержку SSL от поставщика БД для безопасного подключения. Напр. для подключения SSL JDBC к вашему серверу БД. Для этого обратитесь к реестру сервера БД.
ИЗМЕНИТЬ, ЧТОБЫ ЗАКЛЮЧИТЬ keylM comment ON КАК ОБЕСПЕЧИТЬ ПАРОЛЬ JDBC
позволяет сказать, что у вас есть пара частных и открытых ключей: = privare.key и public.cer.
Вы можете зашифровать пароль JDBC с помощью закрытого ключа и сохранить зашифрованный пароль в файле конфигурации. Вы можете использовать OpenSSL для импорта открытого сертификата в файл jks (java keystore) и использовать его в JAVA_HOME\jre\lib\security.
В вашей логике дешифрования: -
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("keystore.jks"),<jks password>); //jks password can be hardoded
Certificate cert= ks.getCertificate(<certificate alias>);
//use certificate to decrypt the encrypted password
Итак, в этом случае: -
хакеру понадобится 3 вещи, чтобы захватить пароль JDBC, что делает систему менее уязвимой:
i) зашифрованный пароль JDBC
ii) Магазин JKS
iii) Пароль магазина JKS
Теперь вы можете задать вопрос о том, как в JKS хранить пароль, ну, будь то ключ, кодовая фраза или пароль, в системе шифрования-дешифрования, по крайней мере, одна вещь должна быть очень безопасной; в противном случае это подвергает опасности всю систему.... в приведенном выше сценарии сертификат может быть предоставлен каждой машине разработчика, чтобы он мог импортировать его в свой файл jks, защищенный одним и тем же паролем магазина jks. Каждый (разработчик) знал бы только пароль магазина JKS, но никогда Пароль JDBC...
Ответ 4
Обычно вы должны аутентифицировать приложение в базе данных sql только под одним именем пользователя, передавая детали пользователя, если это необходимо, в качестве данных в своих запросах, чтобы вы возвращали данные, относящиеся только к этому конечному пользователю. Указывают ли ваши клиенты, что каждый конечный пользователь должен аутентифицироваться в базе данных как отдельный пользователь?
Ответ 5
Чтобы иметь возможность прозрачно шифровать/дешифровать пароли в вашей базе данных с помощью спящего режима, вам необходимо интегрировать что-то вроде Jasypt.
Домашняя страница: www.jasypt.org
См. Раздел: Jasypt + Hibernate 3
Вот как его интегрировать:
<typedef name="encrypted" class="org.jasypt.hibernate.type.EncryptedStringType">
<param name="encryptorRegisteredName">strongHibernateStringEncryptor</param>
</typedef>
<class name="User" table="USER">
<property name="password" column="PASSWORD" type="encrypted" />
<class>
Код >
Ответ 6
Проверьте ответы на похожий вопрос, Как использовать зашифрованный пароль в Apache BasicDataSource?.
Ответ 7
Обычно это обрабатывается с использованием подхода "sysadmin" - с использованием ОС:
Основная концепция - "внешние параметры конфигурации".
Пароли хранятся в открытом тексте в "файле свойств" (который веб-сервер обращается во время выполнения). Пароли защищены путем ограничения доступа к файлам с использованием разрешений на уровне ОС. Как правило, только сотрудники "операций" могут читать/записывать файл, а веб-сервер должен запускаться с правами доступа только для чтения.
Преимущества такого подхода заключаются в следующем:
- Простота понимания и реализации (без ввода зашифрованных значений)
- Защищено программным обеспечением, предназначенным для защиты - это одна из немногих вещей, которые делает ОС (также может быть взломан шифрование, если файл можно прочитать)
- Простая настройка среды разработки/тестирования - просто откройте разрешения для dev/test. Кроме того, только сервер времени выполнения должен иметь надлежащую безопасность.
- Избегает зависимости от библиотек "без бизнес-ценности" (которые не помогают решить вашу бизнес-проблему).
Ответ 8
Вы можете использовать источник данных JDNI на своем сервере приложений, который будет иметь информацию о соединении для базы данных.
Затем вы можете просто сообщить своему приложению через ваш web.xml, чтобы использовать источник данных на сервере веб-приложений.
Вот как я сделал это на Weblogic 9, используя Hibernate 3:
В Hibernate.cfg.xml
<property name="connection.datasource">jdbc/MYJDNINAME</property>
<property name="connection.autocommit">true</property>
<property name="hibernate.connection.release_mode">on_close</property>
В weblogic.xml
<reference-descriptor>
<resource-description>
<res-ref-name>jdbc/MYJDNINAME</res-ref-name>
<jndi-name>MYJDNINAME</jndi-name>
</resource-description>
</reference-descriptor>
Аналогичные решения могут использоваться для серверов tomcat и других серверов приложений:
Инструкции Tomcat 6
Ответ 9
Я получил вашу точку зрения KyleM. Вы можете сделать: