Hibernate authentication without passwords stored in plain text - java

My goal is to authenticate to the database using a JDBC/Hibernate in a secure manner, without storing passwords in plain text. Code examples appreciated. I'm already using waffle to authenticate the user so if there was some way to use the credentials that waffle obtained from the user, and forward those to the DB, that would be good.
Two questions:
What is the recommended way to do multi hop authentication (the client, web server, and database are all different machines) with tomcat/hibernate/spring on web server, a sql database, and obviously client browser?
I would also settle for a way to use a single user account to do authentication, as long as that user account's information was not stored in plain text anywhere. The user account will need both read/write privileges on the DB.
I found some useful information about connecting to SQL Server in this thread. However, I'm expecting that Tomcat will be running under the default account which is like, Local System or something. As far as I know, that account cannot be used to do windows authentication to the database.
My solution:
I did end up using the approach mentioned in the above thread. Instead of running the Tomcat service as Local System it is now running as a user. That user has permission to access the database. My hibernate configuration file is configured as follows:
<property name="hibernate.connection.url">
jdbc:sqlserver://system:port;databaseName=myDb;integratedSecurity=true;
</property>
To those who provided responses
I appreciate everyone's help and I will try out some of the techniques mentioned in the thread. My issue with some of the responses is that they require symmetric encryption which requires a secret key. Keeping the key secret is almost the exact same problem as storing the password in plain text.

i recently blogged about this:
you can tell tomcat's jdbcrealm to use a digest algorithm on the password like sha-256 and save the hash rather than plaintext passwords.
Suppose your User entities look like this:
#Entity
#Table(name = "cr_users")
public class UserDetails{
#Id
#GeneratedValue
private long id;
private String name;
private String passwordHash;
#ManyToMany
private Set<Group> groups;
}
when creating a new User via a service it's possible to create a password hash by using a 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();
}
since SHA-256 will always yield the same hashvalue for the same input you can tell tomcat's JDBCRealm to use this algorithm to verify passwords.
<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"/>
the problem is that tomcat will expect a distinct format for the usertable like this:
+----------------------+ +-------------------+
| tc_realm_users | | tc_realm_groups |
+----------------------+ +-------------------+
| username varchar | | username varchar |
| passwordhash varchar | | groupname varchar |
+----------------------+ +-------------------+
if your user data model fits you're lucky, but my Hibernate generated tables looked like that:
+----------------------+ +-------------------+ +--------------------+
| 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 | +-------------------+ +--------------------+
+----------------------+
so i created a View using SQL which had the expected format and draws it's data from my webapps user data:
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;
with that tomcat was able to authenticate/authorize agains my already existing user data and wrote the data in the context so i could use it in my Jersey (JSR-311) resources:
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();
}
there are also some other Realm implementations out there:
JDBCRealm
DataSourceRealm
JNDIRealm
UserDatabaseRealm
MemoryRealm
JAASRealm
CombinedRealm
LockOutRealm
some links:
http://tomcat.apache.org/tomcat-6.0-doc/realm-howto.html
http://www.ericonjava.com/?p=325
http://objecthunter.congrace.de/tinybo/blog/articles/89

If I understand correctly, your environment is hibernate framework based web app deployed in tomcat.
Now currently you must have configured JDBC passsword
i) either in your hibernate configuration file (generally hibernate.cfg.xml file) in property :-
hibernate.connection.password
ii) or in tomcat configuration file:-
<Resource name="jdbc/myoracle" ......password="tiger".../>
Now you wish to NOT store clear password in any of above files.
In your application code, you must be doing :-
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) One way can be basically:-
You can have your password encrypted using any java security alogirthm using any JCE provider.You store the encrypted password in any of above configuration files (hibernate or tomcat as per your project environment).
and then between Line1 and Line2, you can have decryption logic like:-
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()
You can make encryption and decryption as complex as you wish for e.g. encryption of reverse-string-of-clear-password.
You can use any JCE API:- jasrypt,bouncy castle.
You should need some understanding of java cryptography.Please refer to :-
http://download.oracle.com/javase/1.4.2/docs/guide/security/CryptoSpec.html
2)In case you are concerned about password being transmitting in clear in JDBC connection protocol, then you can use SSL support from DB provider to secure connection. For e.g. to have SSL JDBC connection with your DB server. Refer to your DB server resoruces for this.
EDITED TO CLARIFY keylM's comment ON
HOW TO ENCRYPT THE JDBC PASSWORD
lets say you have a private and public key pair:= privare.key and public.cer.
You can have JDBC password encrypted with private key and save the encrypted password in configuration file. You can use OpenSSL to import public certificate into jks (java keystore) file and have it in your JAVA_HOME\jre\lib\security.
In your decryption logic:-
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
So in this scenario:-
a hacker would need 3 things in order to capture JDBC password which makes system less vulenrable:-
i) encrypted JDBC password
ii) JKS store
iii) JKS store password
You may ask question of then now how about JKS store password, well whether its a key,passphrase or password, in encryption-decryption system, atleast one thing shuld be highly secure; otherwise it jeopradize the whole system....in above scenario, certificate can be given to each developer machine to let him import into his jks file protected by same jks store password..everybody (developer) would know only JKS store password but never JDBC password...

Normally you'd have the app authenticate to the sql database under just one user name, passing the user's details if necessary as data in its queries so that you return data pertinent to just that end user. Have your clients specified that each end user should authenticate to the database as a separate user?

Okay, let's take a look at the problem. You want to have the authentication information made available but not hardcoded anywhere in code or in file system. What I would suggest:
require the administrator of the application to specify the authentication information upon application startup either via jmx or via a webpage that does not require any database connection.
Add a servlet filter to limit access until database authentication information is entered.
This solution does require some extending spring context loading so that it waits until the authentication information is specified (via entry page).

To be able to transparently encrypt/decrypt passwords in your database with hibernate you need to integrate something like Jasypt.
Homepage: www.jasypt.org
See section: Jasypt + Hibernate 3
Here is how to integrate it:
Download jasypt.jar and add it to your runtime classpath
I would suggest using a registered encryptor:
<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>

Usually, this is handled using a "sysadmin" approach - using the OS:
The basic concept is "externalising configuration parameters".
Passwords are stored in plaintext in a "properties file" (that the web server accesses at runtime). The passwords are protected by restricting access to the files using OS-level file permissions. Typically, only "operations" staff can read/write the file, and the web server needs to run with read-only privileges to the file.
The benefits of this approach are:
Simple to understand and implement (no entering encrypted values)
Protected by software designed to protect - it's one of the few things that an OS does (also, encryption can be cracked if the file can be read)
Simple to set up dev/test environments - just open up permissions for dev/test. Also, only the production runtime server needs to have proper security
Avoids dependencies on "no business value" libraries (that don't help solve your business problem)

You can use a JDNI Datasource on your application server that will have the connection information for the Database.
Then you can just tell your application via your web.xml to use the datasource on your web application server.
Here is how I did it on a Weblogic 9 using Hibernate 3:
In 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>
In weblogic.xml
<reference-descriptor>
<resource-description>
<res-ref-name>jdbc/MYJDNINAME</res-ref-name>
<jndi-name>MYJDNINAME</jndi-name>
</resource-description>
</reference-descriptor>
Similar solutions can be used for tomcat and other application servers:
Tomcat 6 Instructions

I got your point KyleM. You can do :
Create plain text file or registry(in case of Windows) place somewhere on other server in encrypted mode.
OR you can use this Lamport's one-time password algorithm

Related

Tomcat SecretKeyCredentialHandler Attributes?

Question:
Does Tomcat 9 Realm <CredentialHandler> need the proper algorithm parameters to hash a password and authenticate a user from a password?
My webapp seems to be able to authenticate a user no matter what parameters I pass the PBKDF2WithHmacSHA512 algorithm, even when the attributes on the <CredentialHandler> element are different, or without any attributes.
Is this the expected behavior of Tomcat's Realm? If so, how can this be? Is the Realm able to deduce the parameters from the composition of the hash?
Background:
Tomcat 9 offers container-managed security with a Realm based on Java's SecretKeyFactory algorithms.
I am using PBKDF2WithHmacSHA512, which has options:
Iterations
Key Length
Salt Length
My CredentialHandler as defined in my webapp's context.xml is
<CredentialHandler
className="org.apache.catalina.realm.SecretKeyCredentialHandler"
algorithm="PBKDF2WithHmacSHA512"
iterations="100000"
keyLength="256"
saltLength="16">
</CredentialHandler>
A Tomcat installation offers CLI access to the hashing algorithm at CATALINA_HOME/bin/digest.[bat|sh]. (See the Tomcat 9 Realm Configuration HOW-To for more details.)
No matter the options I pass to the CLI hashing algorithm, Tomcat is able to authenticate the user correctly from the DataSource Realm (MySQL database). The password can be successfully authenticated against both of the following:
Example #1 matches <CredentialHandler>:
$ $CATALINA_HOME/bin/digest.sh -a PBKDF2WithHmacSHA512 -i 100000 -s
16 -k 256 -h org.apache.catalina.realm.SecretKeyCredentialHandler passw0rd
passw0rd:d0c315b015272b531b0a82cec220d4a1$100000$7ac32ed573fe81e75f611a46622573515
ad11d731dcae4839973ae2702774c51
Example #2 different parameters:
$ $CATALINA_HOME/bin/digest.sh -a PBKDF2WithHmacSHA512 -i 100 -s 1 -k 128
-h org.apache.catalina.realm.SecretKeyCredentialHandler passw0rd
passw0rd:47$100$0e4790b617fa24ee324d55bed38ad4b0
Also See
Tomcat - Understanding CredentialHandler
Yes, this is the expected behavior of Tomcat Realm with a SecretKeyCredentialHandler as CredentialHandler defined. Tomcat does not need the iterations, salt or key parameters in context.xml, to authenticate a user from a password.
How is this possible?
If you look into the tomcat documentation of the SecretKeyCredentialHandler, you will notice that the stored passwords is defined as:
salt $ iterationCount $ encodedCredential - a hex encoded salt, iteration code and a hex encoded credential, each separated by $.
The salt and iterationCount used for encryption are part of the stored password. Tomcat don't uses the values of the CredentialHandler-Tag from context.xml for decryption. It uses the values from the password itself. If you look inside your two generated passwords, you will find salt and iterationCount in it, formated in the defined pattern.
Why do i have to set the values in context.xml anyway?
You don't have to. For decryption, only the algorithm-attribute value from context.xml it used. So, for what are the other attributes? Reading carefully the Tomcat documentation gives the answer:
The CredentialHandler can also be used to generate a new stored version of a given credential that would be required, for example, when adding a new user to a Realm or when changing a user's password.
and
iterations - The number of iterations to use when creating a new
stored credential from a clear text credential.
saltLength - The length of the randomly generated salt to use when
creating a new stored credential from a clear text credential.
keyLength - The length of key to generate for the stored
credential. If not specified, a default of 160 is used.
These CredentialHandler-Tag attributs from context.xml are used when creating a new password via CredentialHandler (see method mutate in API). Its used for encryption to create a new password, not for decryption of existing passwords.

How to keep java JDBC connection to oracle secured

I am building a web application with java and using JDBC driver
please tell me if i wrong, i dont think using this block of code is secured
how should i make it secured without using "HARD CODED" password
Connection connection = null;
connection = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:mkyong","username","password");
connection.close();
taken from:
http://www.mkyong.com/jdbc/connect-to-oracle-db-via-jdbc-driver-java/
If you're building a web-app your resources should be declared externally, for example in a Tomcat Context.xml file. The password will be located there, not in your code. And keeping that secure is a matter of physical security.
Java code will be executed on the server if you mean a Tomcat web application for example. So it should be ok so.
But if you want to have more security you could create a credentials.properties file, with tomcat as owner and rights of 600. Then you read username and password from that file.
And of course you could encrypt it in the file.
EDIT: And the credentials.properties file should not be accessable over the web ;)
Try Jasypt and it should help:
http://jasypt.org/
Example (Using a .properties file mechanism):
Step1: Find out encrypted password and make a note of it for usage:
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
Let's say the encryptedPassword is: G6N718UuyPE5bHyWKyuLQSm02auQPUtm
Step2: Store DB connection credentials in a .properties file with encrypted password
datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost/reportsdb
datasource.username=reportsUser
datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)
Step3: How do we read this value? like this:
/*
* First, create (or ask some other component for) the adequate encryptor for
* decrypting the values in our .properties file.
*/
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("jasypt"); // could be got from web, env variable...
/*
* Create our EncryptableProperties object and load it the usual way.
*/
Properties props = new EncryptableProperties(encryptor);
props.load(new FileInputStream("/path/to/my/configuration.properties"));
/*
* To get a non-encrypted value, we just get it with getProperty...
*/
String datasourceUsername = props.getProperty("datasource.username");
/*
* ...and to get an encrypted value, we do exactly the same. Decryption will
* be transparently performed behind the scenes.
*/
String datasourcePassword = props.getProperty("datasource.password");
// From now on, datasourcePassword equals "reports_passwd"...
For decrypting the encrypted value, we just had to access it with getProperty, just as with any other non-encrypted value.
Details here: http://www.jasypt.org/encrypting-configuration.html

How to save Password and Salt in shiro.ini

currently I work with Shiro to create an authenticated and authorized connection between server and client.
For now I don't have a special realm and only use the shiro.ini to save all my test users.
My question now is if there is a possibility to save the salt for each user in the shiro.ini or do I have to create a seperate database as a realm?
So is there the chance to save it somehow like this within the .ini?
[users]
*username* = *hashed Password*, *salt*, *roles
I actually do not really understand how or where I can save a random salt for each user.
My current understanding is that the 'hashed Password' in the ini is the final comparison value, for the incoming token containing a password that firstly has to be hashed with the user specific salt.

How to log into Business Objects using Active Directory or LDAP

Edit: Appended "or LDAP" to question title to indicate that I would be fine to have a solution which made it possible for me to authenticate with LDAP credentials.
My Question: How do I authenticate a BusinessObjects session using credentials with Active Directory?
Example: I have (I think) an example from SAP on how to do this in .NET but I can't seem to find a similar solution for Java. (See this pdf and search for "Modify the .NET Web Application to enable Kerberos").
Currently: I have a solution to authenticate using an Enterprise Account:
/**
* Logs into BusinessObjects. Sets the reportEngine and biPlatform
*/
public void loginToBusinessObjects() throws AxisFault, MalformedURLException, Exception {
LogHelper.println("Server connection: " + boServer);
URL boConURL = new URL(boServer);//set connection URL
connection = new com.businessobjects.dsws.Connection(boConURL);
boSession = new Session(connection); //setup new session
EnterpriseCredential credential = EnterpriseCredential.Factory.newInstance();
credential.setLogin(boUsername);
credential.setPassword(boPassword);
LogHelper.println(boUsername + ": ##password##");
boSession.login(credential); //login to server
...
}
The code above works great.
Now: I want to be able allow users to give their Active Directory credentials and authenticate using those. I can't seem to find a way to do this however. Documentation on the code above can be found in that same pdf searching for "Logging in to a server."
Note: I could be going about this all wrong. My organization uses the same credentials for Active Directory and LDAP Authentication. If there's a way to do this using LDAP that may be sufficient. Thanks.
The answer assumes you have set up the Active Directory and/or LDAP authentication for users and the user(s) have an alias to that authentication method. This should be verifiable by logins into InfoView.
You should be able to do it by using credential.setAuthType(authType).
Where authType is
"secEnterprise" default value
"secLDAP"
"secWinAD"
Seems and makes sense that by default the AuthType is set to secEnterprise.
Note: I'm still on R3 which has a slightly different authentication mechanism and I have not specifically tried this solution.
Important Edit: The documentation (which is awful for BusinessObjects and anyone reading this probably already knows that) says that for active directory you use "secAD". However, in my testing I was able to successfully authenticate using "secWinAD" which does not appear anywhere in their documentation at all :-/ (that I could find).

Java -> LDAP account password encryption

I have an Ldap directory synchronised from a microsoft active directory.
This Ldap contain many account, each account have a password attribute.
I must develop a java program where a user have to log with his AD login and password, but i don't know the method employed to correctly encrypt the password typed.
I need it to compare with the ldap password.
I also need to bind new account with the same password encryption.
Anyone know how to do?
Well first of all you can use a BIND with SSL, but that's considered kind of the lame way to go about it and may be disabled on some systems. A truly secure way is using SPNEGO-GSS, and this is not trivial. You have to learn and understand about Kerberos. That's a long topic but you can start with reading and going through everything here
I've found the solution with spring,
here the method to test login/pass couple :
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("cn", login));
boolean authentifie = ldapTemplate.authenticate(DistinguishedName.EMPTY_PATH, filter.toString(), password);

Categories

Resources