Need to send a hashed or encrypted password when creating the db connection, see details below:
We have a Spring application that connects to a DB2 AS400 database. We are currently using configuration files (.properties) to store the connection details, Spring reads thes files in the context creation phase and creates the datasource accordingly.
...
database.driverClassName=com.ibm.as400.access.AS400JDBCDriver
database.url=jdbc:as400:<host>:naming=sql;libraries=*LIBL,...;transaction isolation=none
database.username=<user>
database.password=<password>
database.initialPoolSize=2
database.maxPoolSize=5
...
This .properties file lives in the application/web server's file sytem.
I have a requirement to store a hashed password instead of the password directly, that way if someone looks at the file content cannot know what the real password is.
Like this using SHA:
...
database.password=5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
...
There must be a way for telling AS400 that the password being sent is hashed.
In my research I found that AS400 stores passwords using an index QSYUPTBL in the library QSYS, which is able to use DES or SHA hashing algorithms. So it will encrypt the received password and will compare the resulting hash with the one stored in the index. But is it possible to tell the DB's authentication process to expect the password being hashed and compare it directly?
New finding:
The documentation from IBM mentions one keyword: RMTAUTMTH for setting the remote authentication method, using the *ENCRYPTED value in that param will activate the encryption in user id and password:
...User ID and associated encrypted password is sent on a DDM connection request. Cryptographic support must be available on both systems for this authentication method to be used... extracted from http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/cl/chgrdbdire.htm
So it seems that it can be configured in the AS400 side, but does not mention anything about the encryption algorithm being used and if the jdbc driver supports it.
There is no mention of a property in the IBM Toolbox for Java JDBC properties to accept a special hashed/encrypted password.
You are going to have to manage the hashing/encryption of the password within your application and provide it to the JDBC connection as plaintext.
The secure property of the JDBC connection can be used to force an SSL connection to the AS/400, assuming SSL is enabled, to ensure that all data is encrypted between the application and the database.
Related
I have a NIFI image running in openshift and a postgres in the cloud "owned" by another department.
They sent to us a certificate(".crt") and a key(".key"), since the log-in is made trough client certificate, instead of username and password. I have succeeded to log-in in pgAdmin 4, but not to connect the NIFI to the Postgres with certificate and key.
I have uploaded the certificate and the key to the image (using a secret and mounting it) so if I go to the pod terminal I can access it.
But, when I pass to a DBCPConnectionPoll service the connection string bellow and activates an ExecuteSQL processor, I receive an exception that the certificate is not valid, as follows:
ERROR
ExecuteSQL[id=...] Unable to execute SQL query <...>;
due to java.sql.SQLException: Cannot create a PoolableConnectionFactory (FATAL: connection requires a valid client certificate).
No FlowFile to route to failure: org.apache.nifi.processor.exception.ProcessException: java.sql.SQLException: Cannot create a PoolableConnectionFactory (FATAL: connection requires a valid client certificate)
I have tried to pass the certificate in two ways to the DBCPConnectionPoll service:
1) as parameters in the connection string ("database connection url" property:
jdbc:postgresql://<ip>:<port>/<username>?user=<username>&sslTrue&sslcert=/etc/.../mycerts/mycert.der&sslkey=/etc/.../mycerts/mykey.key.pk8
2) adding properties in the service (+ button and the just parameter name and the path as the value) and just passing this as url:
jdbc:postgresql://<ip>:<port>/<username>
Both seems to work generally speaking, since I can connect to another postgres I have which not requires ssl certification.
Some considerations:
1) My assumption here is that the connection string in the NIFI does not know to read properly the file path for the certificate and key.
2) I have converted the certificates a bunch of times to different types that java can receive in order to see if that was the problem, but I still receive the same exception. So it seems that the connection pool just does not "achieve" the files at all. Nevertheless, if some one has a say in this topic, it can be handy, after the main problem is solved. So appreciate some tips here as well.
3) I have also read the NIFI source code and it seems that NIFI uses normally JDBC classes to create the connection pool, so a connection string as I passed would have worked in java code, but somehow doesn't work in NIFI (which is written in java).
4) The jdbc driver and everything else is configured properly, since I can work with a non-secure postgres in NIFI.
Thank you very much.
A co-worker found the 'simple solution' to the problem I asked and I would like to share so it can help others.
What was missing was the property sslmode = require. After including that, the service worked perfectly. Actually, I am not sure why it didn't work with sslmode = prefer, since it is what my pgadmin is using for the same database and there it works perfectly. It seems like we must 'force' nifi to use ssl in this case - see documentation here: https://jdbc.postgresql.org/documentation/head/ssl-client.html.
Moreover, some insights:
It worked with certificate in '.der' and key in '.pk8' formats (didn't have to use trust-store and key-store as needed in other services).
One can add in the 'plus' button the properties and give them the right name as we would do in java code, instead of concatenating every property in the connection string (see second option in the question above).
Make it helps others as well.
I'm trying to have secure passwords within my application but I'm having trouble with the authentication section. The code works for registering a user, generating a salt and hashing a password using the generated salt. But when i try to authenticate I always get a failed login. My problem is how to store the hashed password and salt in a mysql database and how to then retrieve it in java to do the authentication check. Any advice would be hugely appreciated!
I started a project setting up basic authentication. I now want to switch to Digest Authentication. The problem is that the authentication is validated only if I provide the hash of the actual password, and not the actual password.
I did the following to switch from BASIC to DIGEST:
changed in my web.xml the auth-method to DIGEST
changed the JAAS context of my JDBC Realm to "jdbcDigestRealm"
in my db, I used to have "password" as a password, I changed in to the result of MD5(webuser:postgres:webuser) (where webuser is the login, webuser is the password, and postgres is the realm), in other words I set the password in my table to c3c2681ed07a5a2a5cb772061a8385e8.
The problem I have is that the login popup is displayed by the browser when I try to access the resource, but using "webuser" as the password doesn't work. However, using "c3c2681ed07a5a2a5cb772061a8385e8" as the password works. It looks like I'm still in BASIC authentication mode.
Any clue ?
Thank you !
The DIGEST auth-method is same as HTTP Digest Authentication. It just encrypts the communication between the browser and the server. The server still has the password in plain text.
From http://java.boot.by/wcd-guide/ch05s03.html:
The difference between basic and digest authentication is that on the
network connection between the browser and the server, the password is
encrypted, even on a non-SSL connection. In the server, the password
can be stored in clear text or encrypted text, which is true for all
login methods and is independent of the choice that the application
deployer makes.
You should set the digest-algorithm property of your JDBC Realm to MD5. After that the JDBC Realm will hash the password.
Perhaps you may need to change the digest algorithm in the realm view from glassfish console to MD5. Default value from GlassFish 3.0.* is still MD5, but from GlassFish 3.1.* has changed to SHA-256. This could be solution.
Adem
I have a running local instance of PostgreSql on a linux machine. When I use psql command from the shell I success to log in without any problem. I need to connect to the PostgreSql via the JDBC, but I don't know what exactly should I pass as url parameter to DriverManager.getConnection().
It should start with jdbc:postgresql: but what's going next?
I was told by the system group that a database with was created like user name. e.g. if my user is jutky a db named jutky was created, but when I try to open a connection to jdbc:postgresql:jutky I get an error
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "jutky"
:(
Additional info
When I login via the psql I'm not prompted for the password, so when I try to login via JDBC I pass an empty string as a password - is it correct, or should I pass null or something?
When I type psql --help in the shell I see among the rest this line:
Connection options:
-h, --host=HOSTNAME database server host or socket directory (default: "/var/run/postgresql")
So I understand that I connect to PostgreSql via a socket directory, does that matters something to the URL string in the JDBC?
EDIT
First thanks for the answers.
Second: its not first time I'm using JDBC and in particular not the first time I'm connecting to the PostgreSql from JDBC, so I know the general rules and I have read the documentations. However in the described case I'm not sure how exactly should I construct the connection string if the instance is running via the socket directory and what password should I provide. Because when I login via the psql I'm not prompted for password at all.
Thanks in advance.
In addition to other answers note that by default Postgres is configured to accept connections via Unix sockets with authentication based on your operating system account, that's why psql works fine and doesn't require the password.
JDBC connections are made over TCP/IP with password authentication, so you need to modify pg_hba.conf accordingly. For example, this line allows TCP/IP connections from the same machine to all databases for all users with password authentication:
host all all 127.0.0.1/32 md5
After adding this line jdbc:postgresql:databasename should work.
EDIT: You can't create a JDBC connection over Unix socket since PostgreSQL JDBC driver can only work over TCP/IP. The password you use when creating JDBC connection is the password assigned to your user. If you don't have it, you can assign it, for example, using ALTER USER command. See 19.3. Authentication methods.
See also:
19.1. The pg_hba.conf file
It's all explained in official documentation.
This is the relevant part:
String url = "jdbc:postgresql://localhost/test?user=fred&password=secret&ssl=true";
Connection conn = DriverManager.getConnection(url);
We are busy developing a Java web service for a client. There are two possible choices:
Store the encrypted user name / password on the web service client. Read from a config. file on the client side, decrypt and send.
Store the encrypted user name / password on the web server. Read from a config. file on the web server, decrypt and use in the web service.
The user name / password is used by the web service to access a third-party application.
The client already has classes that provide this functionality but this approach involves sending the user name / password in the clear (albeit within the intranet). They would prefer storing the info. within the web service but don't really want to pay for something they already have. (Security is not a big consideration because it's only within their intranet).
So we need something quick and easy in Java.
Any recommendations?
The server is Tomkat 5.5. The web service is Axis2.
What encrypt / decrypt package should we use?
What about a key store?
What configuration mechanism should we use?
Will this be easy to deploy?
As I understand anyhow in order to call 3rd party web service you pass password as plain text and no security certificates are involved.
Then I would say the easiest approach would be to store password in encrypted format (via java encryption mechanism) when the encryption/decryption key is just hard coded in the code.
I would definitely store it on the server side (file system or db) rather then distribute and maintain it on the multiple clients.
Here is how that could work with "DES" encryption:
// only the first 8 Bytes of the constructor argument are used
// as material for generating the keySpec
DESKeySpec keySpec = new DESKeySpec("YourSecr".getBytes("UTF8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
sun.misc.BASE64Encoder base64encoder = new BASE64Encoder();
sun.misc.BASE64Decoder base64decoder = new BASE64Decoder();
.........
// ENCODE plainTextPassword String
byte[] cleartext = plainTextPassword.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("DES"); // cipher is not thread safe
cipher.init(Cipher.ENCRYPT_MODE, key);
String encrypedPwd = base64encoder.encode(cipher.doFinal(cleartext));
// now you can store it
......
// DECODE encryptedPwd String
byte[] encrypedPwdBytes = base64decoder.decodeBuffer(encryptedPwd);
Cipher cipher = Cipher.getInstance("DES");// cipher is not thread safe
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainTextPwdBytes = (cipher.doFinal(encrypedPwdBytes));
Being on the intranet certainly does not justify dismissing security. Most damage done to information is by insiders. Look at the value of what's being protected, and give due consideration to security.
It sounds like there's a third-party application, for which you have one set of credentials, and some clients that effectively share this identity when using the third-party application. If that's the case, I recommend the following approach.
Don't distribute the third-party password beyond your web server.
The safest way to do this is to provide it to the web application interactively. This could be ServletContextListener that prompts for the password as the application starts, or a page in the application so that a admin can enter it through a form. The password is stored in the ServletContext and used to authenticate requests to the third-party service.
A step down in safety is to store the password on the server's file system so that it's readable only by the user running the server. This relies on the server's file system permissions for protection.
Trying to store an encrypted form of the password, on the client or the server, is just taking one step backward. You fall into an infinite regress when trying to protect a secret with another secret.
In addition, the clients should authenticate themselves to the server. If the client is interactive, have the users enter a password. The server can then decide if that user is authorized to access the third-party service. If the client is not interactive, the next best security is to protect the client's password using file system permissions.
To protect the clients' credentials, the channel between the client and your web server should be protected with SSL. Here, operating on an intranet is advantageous, because you can use a self-signed certificate on the server.
If you do store passwords in a file, put them in a file by themselves; it makes the need to manage permissions carefully more conspicuous, and minimizes the need for many users to be editing that file and thus seeing the password.