休眠身份验证,不以纯文本形式存储密码

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

两个问题:

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

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

我的解决方案

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

    <property name="hibernate.connection.url">
jdbc:sqlserver://system:port;databaseName=myDb;integratedSecurity=true;
</property>

致那些提供答复的人

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


答案 1

我最近写了一篇关于这个的博客

你可以告诉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将始终为相同的输入产生相同的哈希值,因此您可以告诉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 |  +-------------------+  +--------------------+
+----------------------+

所以我使用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
  • 用户数据库Realm
  • 记忆现实
  • 亚斯雷亚姆
  • 组合现实
  • 锁定现实

一些链接:


答案 2

如果我理解正确,你的环境是部署在tomcat中的基于休眠框架的Web应用程序。

现在,您必须在属性中配置 JDBC passsword i) 在休眠配置文件(通常是休眠.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)一种方法基本上可以是:- 您可以使用任何JCE提供程序使用任何java安全alogirthm加密密码。您将加密的密码存储在上述任何配置文件中(根据您的项目环境休眠或tomcat)。

然后在第1行和第2行之间,您可以拥有解密逻辑,例如:-

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()

您可以根据需要使加密和解密变得复杂,例如,反向字符串清除密码的加密。您可以使用任何JCE API:- jasrypt,充气城堡。你应该需要对Java密码学有一些了解。请参考:-

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

2)如果您担心密码在JDBC连接协议中以明文形式传输,那么您可以使用数据库提供程序的SSL支持来保护连接。例如,与数据库服务器建立 SSL JDBC 连接。请参阅数据库服务器资源。

编辑以澄清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

因此,在这种情况下:- 黑客需要3件事才能捕获JDBC密码,从而使系统不那么易受攻击:- i)加密的JDBC密码ii)JKS存储iii)JKS存储密码

你可能会问,现在JKS存储密码怎么样,好吧,无论是密钥,密码还是密码,在加密 - 解密系统中,至少有一件事应该是高度安全的;否则,它将整个系统放大....在上面的场景中,可以向每个开发人员计算机提供证书,让他导入到受相同jks存储密码保护的jks文件中。每个人(开发人员)只会知道JKS存储密码,但永远不会知道JDBC密码...


推荐