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.
Related
I can create user using Boomi without any SSL(means using port389) and password(for new user to login) but I want to change user's phone number, and I got this error:
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 00000057: LdapErr: DSID-0C042612, comment: Error in attribute conversion operation, data 0, v4563?]; remaining name ''???at java.naming/com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3332)???at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3205)???at java.naming/com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2996)???at java.naming/com.sun.jndi.ldap.LdapCtx.c_modifyAttributes(LdapCtx.java:1504)???at java.naming/com.sun.jndi.toolkit.ctx.ComponentDirContext.p_modifyAttributes(ComponentDirContext.java:277)???at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.modifyAttributes(PartialCompositeDirContext.java:192)???at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.modifyAttributes(PartialCompositeDirContext.java:181)???at java.naming/javax.naming.directory.InitialDirContext.modifyAttributes(InitialDirContext.java:167)???at com.boomi.connector.LDAP.LDAPConnection.updateObject(LDAPConnection.java:190)???at com.boomi.connector.LDAP.LDAPUpdateOperation.executeUpdate(LDAPUpdateOperation.java:227)???at com.boomi.connector.util.BaseUpdateOperation.execute(BaseUpdateOperation.java:30)???at com.boomi.connector.generic.GenericConnectorAction.invoke(GenericConnectorAction.java:189)???at com.boomi.connector.generic.GenericConnectorAction.invoke(GenericConnectorAction.java:172)???at com.boomi.connector.base.BaseConnectorAction.invokeBase(BaseConnectorAction.java:368)???at com.boomi.connector.base.BaseConnectorAction.invokeWithReadStore(BaseConnectorAction.java:304)???at com.boomi.connector.base.BaseConnectorAction.invoke(BaseConnectorAction.java:276)???at jdk.internal.reflect.GeneratedMethodAccessor29.invoke(Unknown Source)???
and I googled this error means:
Indicates that the LDAP server cannot process the request because of server-defined restrictions. This error is returned for the following reasons:
1. The add entry request violates the server's structure rules
2. The modify attribute request specifies attributes that users cannot modify -> I just want to change phone number.
3. Password restrictions prevent the action
4. Connection restrictions prevent the action. -> I think I can create user, so the connection is fine!
If LDAP error Code 53 means I need to do everything with "SSL", why I can create a user? How can I sort this problem out?
It's possible the user actually hasn't been properly provisioned. Maybe it's been created, but the account is not enabled because the password is invalid. Run an LDAP client and try and logon as the new user with the credentials that you supplied. Does it work?
Two suggestions:
Please use LDAPS if you're creating users/setting passwords, or basically anything in AD LDAP. There's no excuse not to these days, and Microsoft is in the process of deprecating plain LDAP from non-Windows clients, so you might as well do it now. All you need is to install/trust the issuing CA certificate chain (root and intermediate certs) for whatever CA is signing the DC's LDAPS certificate. You do not need to install any client LDAP cert.
Check the password policy of the target domain and ensure the passwords you are trying to set meet the requirements in terms of complexity, length and so on.
I have read this two posts: One and Two, but I still have question.
I use KeyStore (Android 9) to generate an AES key, and use isInsideSecureHardware() method to check whether the key isInsideSecureHardware. I got return False. Sample code can be found here, and here.
public boolean isInsideSecureHardware ()
Returns true if the key resides inside secure hardware (e.g., Trusted Execution Environment (TEE) or Secure Element (SE)). Key material of such keys is available in plaintext only inside the secure hardware and is not exposed outside of it.
Thus, I want to further confirm whether my phone device (Huawei P20) supports TEE.
Question:
If the phone supports TEE, the key generated by KeyStore will be store into TEE automatically? Do I Need any manually configuration in Java? I heard that keys will be automatically stored in TEE, as long as you use KeyStore.getInstance(), KeyGenerator
.getInstance(algorithm, KeyStore Name). But I am not sure this is True or Not?
If the answer of Q1 is "Need manually configuration", it becomes the reason of isInsideSecureHardware() returns False, right? If the answer of Q1 is "automatically", ignore Q2.
Any method to directly check whether the phone supports TEE, in Java?
#JensV is correct: if you set setIsStrongBoxBacked on the keyGenParameterSpecBuilder, key generation will fail with a StrongBoxUnavailableException if StrongBox is not supported. However, the intermediate case - where there is a TEE (i.e. keys are generated and used within secure HW), but no support for StrongBox - is more tricky to discern.
In general, the way to go is to actually generate a key on the device, and then perform HW key attestation on it at the server - consulting the signed key properties to examine the exact degree of HW backing:
generate a nonce (random byte string) ON The SERVER, pass it to the device
generate a key on the device, requesting HW attestation by calling setAttestationChallenge on the KeyGenParameterSpec builder and passing in the nonce you get from the server (DO NOT USE A NONCE PRODUCED ON THE DEVICE)
request the attestation chain for the key from the Android Key Store
pass the attestation data (cert chain) to your server
verify the attestation (signature) chain on your server
confirm that the root cert matches a published Google root cert
confirm that no cert in the chain hasn been revoked (check against CRL # https://android.googleapis.com/attestation/status)
examine the properties of the Google Key Attestation extension (OID 1.3.6.1.4.1.11129.2.1.17) of the leaf cert
confirm the nonce matches (attestationChallenge)
consult the attestationSecurityLevel of KeyDescription
SecurityLevel ::= ENUMERATED {
Software (0),
TrustedEnvironment (1),
StrongBox (2),
}
TrustedEnvironment and StrongBox both correspond to hardware-backed keys and crypto operations.
From the Android keystore system docs:
Supported devices running Android 9 (API level 28) or higher installed can have a StrongBox Keymaster, an implementation of the Keymaster HAL that resides in a hardware security module. The module contains the following:
[...]
* Secure storage.
[...]
When checking keys stored in the StrongBox Keymaster, the system corroborates a key's integrity with the Trusted Execution Environment (TEE).
[...]
When generating or importing keys using the KeyStore class, you indicate a preference for storing the key in the StrongBox Keymaster by passing true to the setIsStrongBoxBacked() method.
In my understanding that means when you generate a Key and call keyGenParameterSpecBuilder.setIsStrongBoxBacked(true) for the key configuration you can ensure that it's backed by a TEE. If there is no TEE available, it'll throw a StrongBoxUnavailableException.
So to check if there's a TEE available you could just attempt to generate a key this way and see if it works.
This might be an old question but i still didn't find proper answer for this question, so please be patient.
I have a https login page,which is using a form post method and sending the credentials to the server...blah blah.
At the time of login, if you use IE and F12 for network monitoring, click start capturing. You can see some URL which has similar to login, servetloginauth(from gmail.com) and you can see the request body with your username and password.
Okay, one can argue, that only if the user didn't logout you can see that.
Now logout and don't close the browser and get browser dump(any browser, any version) off of Task Manager(i'm not sure how to do the same in Mac).
Use WinHex editor to open the dump file and do Search/Find: "password=" or the actual password(since u r testing your own login, you already knew your password).
You can see the password in clear text.
Now my question is, How can i mask the password:
1. Either in the Post request URL
2. Or when the browser is saving my credentials to the dump, i neeed it to be masked/encrypted or should not save the password at all.
My code for jsp:
<s:form id="login" name="loginForm1" action="login" namespace="/" method="post" enctype="multipart/form-data" >
<fieldset><!-- login fieldset -->
<div><!-- div inside login fieldset -->
<div....
<label for="password" class="loginLabel">Password</label>
<input type="password" name="password" id="password" class="longField nofull absPosition" size="16" autocomplete="off" alt="Password" placeholder="Password" title="Password|<
Current solution i have as below, but i need any alternatives without much effort.
The password can be read from the memory if it is being sent as
cleartext. Using the salted hash technique for password transmission
will resolve this issue. Hashing is a cryptographic technique in which
the actual value can never be recovered. In the salted hash technique,
the passwords are stored as hashes in the database. The server
generates a random string, salt, and sends it along with the Login
page to the client. A JavaScript code on the page computes a hash of
the entered password, concatenates the salt and computes a hash of the
entire string. This value is sent to the server in the POST request.
The server then retrieves the user's hashed password from the
database, concatenates the same salt and computes a hash. If the user
had entered the correct password, these two hashes should match.
Now, the POST request will contain the salted hash value of the
password and the cleartext password will not be present in the memory
SHA 256 is a strong hashing algorithm available today – readymade
implementations in JavaScript are available and quoted in the "Good
Reads" section.
Note: For pages containing sensitive information or pages wherein data
can be modified in the database, use JavaScript to flush the memory of
the browse
and the images are as below.
On an additional note, i can settle with something Citibank did for their customers on their website.
I logged in the website and in the dump i see my username is masked(as it appears in the website), i need something which does the same to the password field too. can someone explain me how to do it please.
What you are suggesting has a serious security flaw. If you calculate the hash on the browser and then send to the server (without the password) then the server can't trust that the browser actually calculated the hash. A hacker might merely have read the file of hash values and construct a program to send the hash value in. The security comes from the server (a trusted environment) having the password which can not be guessed from the hash, and then proving to itself that the password produces the hash.
If you send both the hash and the password, then you have not solved your problem about the password being available in clear text.
There would seem to be a way if you hash the password multiple times. You can hash the password once (or more times) on the browser, and use that for subsequent hashing calls on the server. It seems normal to hash multiple times (although it is unclear how much this really makes it more secure). The point is that the browser would be holding an intermediate value which would not tell you the password that the user typed. It would, however, still tell you the value that you need to send to the server to authenticate the user. That value is infact a proxy for the password, and is usable as a password in calls to the server. But ... it is not the password that the user typed in.
One final way looks that it might work: use an asymmetric encryption. The server provides a salt value and a public key. The password is encrypted using the public key, which can only be decrypted by the private key that is held on the server. Because the salt value changes every session, the encrypted value held in memory itself would not be usable across another session. The server decrypts the value, extracts the salt, giving it the password from which to go ahead and do password authentication.
You have to device for how the passwords are stored in the database. There are multiple ways to do this, but there is no way you can create anything that is IMPOSSIBLE to hack/read.
However, you can limit MITM attacks by hashing the password X number of times before sending it to the server.
When the hash is recived by the server, you do X number of new hash rounds. You should also figure out a how to manage your salt.
This should be sufficient for most applications. Also this is how most application does it these days.
gpEasy: http://gpeasy.com/ does this by hasing Sha-256, 50 times on client side. Then another 950 rounds on the server. In total 1000 rounds. This also includes a salt which is calculated by its "current hash"
def hash(self, pw, loops = 50):
pw = pw.strip()
for i in range(loops):
salt_len = re.sub(r'[a-f]', '', pw)
try:
salt_start = int(salt_len[0:0+1])
except ValueError:
salt_start = 0
try:
salt_len = int(salt_len[2:2+1])
except ValueError:
salt_len = 0
salt = pw[salt_start:salt_start+salt_len]
pw = hashlib.sha512(pw.encode('utf-8') + salt.encode('utf-8')).hexdigest()
return pw
This is a version of the mentioned algorithm for calculating hash with a salt from the first numbers in the hash.
I have been storing my passwords in plain-text for development purposes but want to start storing the hashes instead, but have so far not yet succeeded in having GlassFish properly authenticate against a hashed password due to the following SecurityException:
SEVERE: jdbcrealm.invaliduserreason
WARNING: WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Security Exception
First off, I am running GlassFish 3.1 and have setup the digest for my JDBC realm to SHA-256.
My User class has the following annotated password field:
#Basic(fetch = FetchType.LAZY)
#Column(length = 45, nullable = false)
private String password;
The following helper method is responsible for hashing the password:
private byte[] digest(String input) {
byte[] output = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
output = md.digest(input.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(RegistrationController.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(RegistrationController.class.getName()).log(Level.SEVERE, null, ex);
}
return output;
}
I then set the password on the user as follows:
u.setPassword(Base64.encode(digest(password)).toString());
I wouldn't have Base64 encoded the because this seems to be undocumented but this question: Glassfish Security - jdbcRealm: How to configure login with SHA-256 digest suggests you do need to do so.
So I guess what I would like to know is, does GlassFish expect a String (VARCHAR) or a byte[] (BLOB) as the password field in the database, am I correctly hashing the password, and is it correct to additionally Base64 encode the password hash?
Thanks!
Does GlassFish expect a String (VARCHAR) or a byte[] (BLOB) as the password field in the database?
It expects a column that maps to the Java Type java.lang.String in JDBC, and those would typically be CHAR, VARCHAR etc. LOBs would not work as the JDBC realm implementation issues a ResultSet.getString method call invocation to obtain the password hash.
Am I correctly hashing the password, and is it correct to additionally Base64 encode the password hash?
Base64 encoding is not the only supported option. You can perform Hex encoding as well. But you must perform either of these, and configure the JDBC Realm to perform the same at runtime. In the absence of an encoding parameter, Glassfish will convert the byte sequence associated with the digest, to a sequence of characters in the charset configured for the realm.
I suspect the problem has something to do with the mention of UTF-8 encoding in the expression input.getBytes("UTF-8"). It would be worth verifying if the Base64 encoding of the result provided by your digest method actually matches the password hashes stored in the database.
Also, considering the reason provided for the failure being jdbcrealm.invaliduserreason, I would also suspect that one of the following conditions might be true:
The encoding parameter is not specified for the JDBC Realm; it should preferably be one of base64 or hex (the case does not matter, going by the source code of the JDBC realm), otherwise you would end up in the scenario where the digest byte array is converted to a character array (which in my opinion is a bit flaky unless you can guarantee that the password provided by users are always in a particular encoding).
No password hash exists for the user in the database. See my previous answer on the SQL query executed; you might want to run the query yourself. You can log the statements issued by Derby (if you are using it as the database), by placing a file named derby.properties in the location of your Derby database with the property derby.language.logStatementText=true in it. On shutting down the database, the derby.log file will be populated with all the queries issued by the application server.
The SQL statement prepared by Glassfish is incorrect.
A connection to the database could not be established.
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