没有以明文形式存储的密码的Hibernate身份验证

Kyl*_*leM 15 java sql hibernate windows-authentication

我的目标是以安全的方式使用JDBC/Hibernate对数据库进行身份验证,而无需以纯文本形式存储密码.代码示例赞赏.我已经在使用华夫饼干对用户进行身份验证,因此如果有某种方法可以使用从用户那里获得的凭证,并将其转发给数据库,那就太好了.

两个问题:

  1. 在Web服务器,sql数据库和显然客户端浏览器上使用tomcat/hibernate/spring进行多跳认证(客户端,Web服务器和数据库都是不同的机器)的推荐方法是什么?
  2. 我还想找到一种方法来使用单个用户帐户进行身份验证,只要该用户帐户的信息不是以纯文本形式存储在任何地方.用户帐户将需要DB上的读/写权限.

我找到了一些有关在此线程中连接到SQL Server的有用信息.但是,我期待Tomcat将在默认帐户下运行,例如本地系统或其他东西.据我所知,该帐户不能用于对数据库进行Windows身份验证.

我的解决方案:

我最终使用上述线程中提到的方法.它不是将Tomcat服务作为本地系统运行,而是以用户身份运行.该用户有权访问数据库.我的hibernate配置文件配置如下:

    <property name="hibernate.connection.url">
jdbc:sqlserver://system:port;databaseName=myDb;integratedSecurity=true;
</property>
Run Code Online (Sandbox Code Playgroud)

对那些提供回复的人

我感谢大家的帮助,我将尝试一些线程中提到的技术.我对某些响应的问题是它们需要对称加密,这需要一个密钥.保持密钥的秘密几乎与将密码以纯文本格式存储完全相同.

fas*_*seg 14

我最近在博客上写到:

你可以告诉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;
}
Run Code Online (Sandbox Code Playgroud)

通过服务创建新用户时,可以使用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();
}
Run Code Online (Sandbox Code Playgroud)

由于SHA-256将始终为相同的输入产生相同的哈希值,因此您可以告诉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"/>
Run Code Online (Sandbox Code Playgroud)

问题是tomcat会期望使用这样的usertable的不同格式:

+----------------------+  +-------------------+
|   tc_realm_users     |  | tc_realm_groups   |
+----------------------+  +-------------------+
| username     varchar |  | username  varchar |
| passwordhash varchar |  | groupname varchar |
+----------------------+  +-------------------+
Run Code Online (Sandbox Code Playgroud)

如果您的用户数据模型适合您,但我的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 |  +-------------------+  +--------------------+
+----------------------+
Run Code Online (Sandbox Code Playgroud)

所以我创建了一个使用SQL 的View,它具有预期的格式并从我的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;
Run Code Online (Sandbox Code Playgroud)

因为tomcat能够再次验证/授权我已经存在的用户数据并在上下文中写入数据,所以我可以在我的Jersey(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();
        }
Run Code Online (Sandbox Code Playgroud)

还有其他一些Realm实现:

  • JDBCRealm
  • DataSourceRealm
  • JNDIRealm
  • 是UserDatabaseRealm
  • MemoryRealm
  • JAASRealm
  • CombinedRealm
  • LockOutRealm

一些链接:


ag1*_*112 7

如果我理解正确,你的环境是在tomcat中部署的基于hibernate框架的web应用程序.

现在你必须已经在你的hibernate配置文件(通常是hibernate.cfg.xml文件)中的属性中配置了JDBC passsword: -

hibernate.connection.password

ii)或在tomcat配置文件中: -

<Resource name="jdbc/myoracle" ......password="tiger".../>
Run Code Online (Sandbox Code Playgroud)

现在您不希望在上述任何文件中存储明确的密码.

在您的应用程序代码中,您必须这样做: -

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.
Run Code Online (Sandbox Code Playgroud)

1)一种方法基本上可以: - 您可以使用任何JCE提供程序使用任何Java安全alogirthm加密您的密码.您可以将加密密码存储在上述任何配置文件中(根据您的项目环境,hibernate或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()
Run Code Online (Sandbox Code Playgroud)

您可以根据需要使加密和解密变得复杂,例如加密反向串清除密码.您可以使用任何JCE API: - jasrypt,bouncy castle.您应该需要了解java加密.请参阅: -

http://download.oracle.com/javase/1.4.2/docs/guide/security/CryptoSpec.html

2)如果您担心在JDBC连接协议中明确传输密码,则可以使用DB提供商提供的SSL支持来保护连接.例如,与您的数据库服务器建立SSL JDBC连接.请参阅您的数据库服务器resoruces.

编辑澄清keylM关于如何加密JDBC密码的评论

假设你有一个私钥和公钥对:= privare.key和public.cer.您可以使用私钥加密JDBC密码,并将加密密码保存在配置文件中.您可以使用OpenSSL将公共证书导入jks(java密钥库)文件,并将其放在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
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下: - 黑客需要3件事才能捕获JDBC密码,这会使系统变得不那么虚荣: - i)加密的JDBC密码ii)JKS商店iii)JKS商店密码

你现在可能会问一个问题,那就是JKS存储密码,无论是密钥,密码还是密码,在加密 - 解密系统中,至少有一件事是高度安全的; 否则它会影响整个系统....在上面的场景中,可以给每个开发者机器提供证书,让他导入到受同一jks商店密码保护的jks文件中.所有人(开发人员)只知道JKS商店密码但从不JDBC密码......


Mat*_*ran 5

通常,您只需要在一个用户名下对sql数据库进行身份验证,并在必要时将用户的详细信息作为数据传递给查询,以便返回与该最终用户相关的数据.让您的客户指定每个最终用户应该作为单独的用户对数据库进行身份验证吗?

  • 好吧,通常你可能让你的一个数据库用户将密码信息存储为文本文件中的哈希字符串,但是你可以通过Web服务器在文件结构之外的某个地方使用该文本文件,因此它不会暴露给外部世界.当用户以app用户身份登录时,应用程序将查询数据库上的用户名表,以查看他/她是否为注册用户.应用用户无需了解数据库用户凭据,只需了解应用用户凭据.如果您正在使用Spring,这可能是一个有用的文档:http://s.coop/15r7 (2认同)

Pie*_*rre 2

好吧,我们来看看问题所在。您希望身份验证信息可用,但不硬编码在代码或文件系统中的任何位置。我的建议是:

  • 要求应用程序的管理员在应用程序启动时通过 jmx 或不需要任何数据库连接的网页指定身份验证信息。
  • 添加 servlet 过滤器以限制访问,直到输入数据库身份验证信息。

该解决方案确实需要一些扩展的 spring 上下文加载,以便它等到指定身份验证信息(通过入口页面)。

  • 尽管这看起来有点愚蠢......这是问题的唯一真正答案 (3认同)