0

I'm currently working on a desktop application in Java (using JavaFX).

This application stores some user information, parts of which are sensitive. For example, if the user configures a proxy, it will store it into a file with encryption. Moreover, my application is calling a web API to execute auto-updates (with a basic authentication scheme).

Currently my application is closed source, and is used in a context that doesn't require high security. The implementation basically contains all the credentials needed to authenticate in the API, or to encrypt user settings. Basically, it contains something like (simplified, it's not "that obvious").

String login = "mysuperlogin", password = "mysuperpassword";
authenticateToApi(login, password);

String encryptionKey = "mysuperencryptionkey";
encryptDataToFile(data, file, encryptionKey);

But: I know that this way of doing things is not secure at all! I know that it's possible to "decompile" Java and see the credentials/methods used by the application, and then use them to gain access to the "sensitive" information.

I'm planning to open-source my project this year, so I know I have to get the things done to provide a better security level, here are the options I see:

  • Using properties filled on build time to include my credentials directly in the code, without including them into the source: this doesn't solve the "decompile" problem
  • Using a key entered by user to secure every information: I don't like this solution because my application needs to be used with the lowest user "technical" intervention. Moreover, this doesn't solve the API credentials problems.
  • Generate a key from the current system to encrypt data. But in open-source version, it will be possible to find the current system key, and then use it to access encrypted data.

I suppose that they are a lot of closed/open source programs that have this problems, but I can't figure out what's the "least worst" solution.

  • It is not clear to me what problem you want to solve. It looks like you want to allow users to use your credentials to access the API while at the same time keeping those credentials hidden from the user. I am right? – Goyo Mar 2 '18 at 10:35
  • 1
    Since Java is fully decompilable, you should create user credentials, with permissions, so the users logs in with their credentials and you don't share the super admin credentials. – Nurrl Mar 2 '18 at 11:25
3

The only way to ensure that the credentials used by a particular installation of your program cannot be abused is:

A) Use a private key that only you know to generate a password hash and not an actual password.

B) Add a random component in generated password hash such that each installation will have its own password hash.

C) Make the password work exactly once on your server. After first login, require that a username and password be created.

D) Make the program fail if this initial validation fails.

Use a private key

By using a private key, the hash that gets generated is one-way and only you can convert a password to its proper hash. This means that the client, despite knowing how the program works and knowing the exact password hash, cannot ever hope to generate his own password hash for others to use in other installations. The generated hash can only be sent to your server for validation.

Add a random component

If you use a random component in addition to the private key, you can not only ensure that only you can authenticate it, but you can know exactly which installation it is. When you generate the hash, you use your private key and a random unique id, and you associate both with the username on your private server. When the client software sends that username and the generated hash, you can use the private key and the associated random component to hopefully get the hash passed and compare them.

Make the password work exactly once

This is the final nail in the coffin and ensures that they can't copy and paste the entire installation on another computer and install it there. If you want to be able to enable say, 100 possible installations for a company, then you can even make the same generated key work 100 times. Each time your server receives a request, you decrement that counter and once it hits 0, your server responds that the program has exhausted all licenses.

Of course for this to work, it stands to reason that the first time the program is used, it asks for a username and password. If the generated key is valid, then client should follow up with a username and password to associate with that key/installation. They can then login each successive time for that installation using the username and password provided.

Make the program fail

Should the validation fail, the user should not be able to so much as provide credentials to enter the program. You should output the proper error indicative of the type of problem encountered with a message that the user should contact the administrator or send a mail at this address.

Admittedly, using this method means the user must have access to the internet, but it is the only way to be sure. You cannot rely on the computer of installation for validation. Even say, the serial number of the hard drive can be falsified by running the installation on a virtual machine. However to the end user, this would all be transparent except for the minor additive that the computer of the installation must have access to the internet, and in all likelihood this should not cause any problems.

  • I have another example to ask : when I use a software like "PgAdmin" (database client), I have an option when I'm connecting to my database to "Save my password". Is this "Save my password" option using this kind of solution ? I suppose (and hope!) that PgAdmin doesn't store my DB password in a clear text file on my computer. I'm having trouble exactly understanding how this work in this kind of situation. – zimath Mar 3 '18 at 21:09
  • @zimath Well honestly I don't know how PgAdmin works personally. Generally you'd only save a hash of a password so that you could never figure out the password from its hash, then send the hash to the server not the actual password. Though for a database, maybe you'd have to use two-way encryption, which means you could in theory determine the password if you knew how the program worked. Though we're also talking about a user to the database with its own permissions and limitations. The harm is only as bad as the level of permissions of the user. It isn't user/password to server itself. – Neil Mar 5 '18 at 7:38
0

I'm going to try and simplify your problem statement -- you appear to be storing user-entered credentials in a similar way a web browser does for saved passwords.

By default web-browser saved passwords are vulnerable in the same way your saved credentials will be. And you can use the same solution web-browsers do - a master password to secure your credential store.

When your application starts up for the first time, require the user to specify a master password.

Use this master password as the key to a keystore[1] in which to hold all your saved credentials in an encrypted form (possibly using the Java KeyStore classes, or something else of your choosing).

Do not ever save the user-entered master password to disk.

When your application starts up thereafter, it will need to ask the user for their master password in order to open the keystore and decrypt the saved credentials.

Since the master password is not known to your software (or anything else - we need to assume the local machine is free from keyloggers or rootkits etc which would mean all bets are off), and not held anywhere on disk, then the keystore should be reasonable secure.

The downside is that the user has to re-enter this password every time your application is launched, and if they forget it, then the credential store is lost.


[1] I'm skipping over lots of detail here in terms of salting, encryption types etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.