7

I'm working on a software that need to store and use secrets.

These secrets can be for example:

  • a password to connect to a database
  • a client secret for an OAuth 2.0 client_credentials grant.

I need to store these passwords somewhere (preferably in some kind of configuration file) and protect them with reasonable security.

Reasonable security means: no heavy hardware based solution such as HSM or a complicate infrastructure (PKI, ...).

The software is in Java, it should run anywhere Java can run (Especially Windows, GNU/Linux, Solaris, AIX).

It cannot rely on a OS or distribution specific tool such as GNOME keystore, KDE Wallet or any of these unless Java provides way to handle them (but I doubt it).

I thought of using symetric cryptography to encrypt the secrets when configuring the software and decrypt them at runtime when needed. But I don't know how to handle the encryption key.

I have 4 found possible solutions:

  1. Use PBKDF2 (SHA256-AES256-CBC) or plain AES256-CBC with an hardcoded cryptographic key and optionally some code obfuscation (with ProGuard)
    • There is a dedicated CWE about that: CWE-321 - Use of Hard-coded Cryptographic Key. So I doubt this is the solution.
  2. Use PBKDF2 (SHA256-AES256-CBC) or plain AES256-CBC with a key in a file on the filesystem
    • It exposes the key in a file that should be protected by some way
      • encrypted filesystem and/or
      • enforced ACL
      • anything else?
  3. Use the Java keystore
    • The Java keystore needs a password to unlock, so where to store the Java keystore password? How to protect it? The serpent is eating its own tail...
    • Somehow equivalent to 2
  4. Use white-box cryptography
    • It has shown some weaknesses and is more or less equivalent to obfuscation. (which is inside option 1).
    • I'm not sure Java implementation of WBC are mature.

I've seen that question: Practices for storing username/password in Web applications.

So my question here is: What is the "best" resonable option to protect my secret data?

I feel that 2 is better, after all it is simple, and the secret key protection would rely on the OS rather than the software itself, just like OpenSSH but I'm not totally convinced.

EDIT:

PBKDF2 could be replaced by any other password base key derivation algorithm, such as PKCS #12 v1 key derivation algorithm.

Bonus:

If I were to choose between PBKDF2 (SHA256-AES256-CBC) or plain AES256-CBC, what would be the best?

  • PBKDF2 is said to be good to protect passwords because it is 'slow'. And it was desgined for that.
  • AES256-CBC is more simple (no need to salt/hash/iterate the key like in PBKDF2)
  • I'm a little confused because you talk about storing OAuth credentials or passwords for connecting to another database, and decrypting them later to use, but then you talk about hash algorithms. Hash algorithms are one-way, i.e. you won't be able to get the secrets back from output after you hash them, even with some sort of secret key. – Ben Jan 21 '16 at 15:53
  • PBKDF2 can be use to generate a 256bits key that is used for an AES256. It's called password based encryption. In that case SHA256 is used as an hashing algorithm to help generate this key. In the end, the data would be encrypted by an AES256. – superbob Jan 21 '16 at 16:16
2

I'm assuming that your threat model is an attacker gaining access to read files on your webserver via some type of web exploit. If not, you should question what exactly you are mitigating with your proposed encryption strategy.

If it is, an option similar to 2 is probably the most simple.

I would use AES128-CBC as the algorithm for a Key Encrypting Key. With a CSPRNG generated 128 bit encryption key, there is no need to use a key stretching algorithm. Algorithms such as PBKDF2 are only needed when you are attempting to secure weak, user supplied passwords. If you can set the keys yourself, you can simply make sure that they are the required bit strength.

AES256 is slower, and with no additional security over AES128.

Make sure your configuration file and your key file is locked down by access control lists.

Key file:

<128 bit random key>

Can only be read by a custom service account.

Config file:

Passwords to systems, encrypted by the key in the Key file.

Can only be read by the web server identity.


Make sure these files aren't servable by your web server (e.g. example.com/config.txt won't work). The way this should work is that you have a service account that runs a process. The web service calls this process and asks the service to decrypt a configuration file password for it. The service validates that only the web server process can call it.

This will protect your secrets from any LFI exploits in your system or server because an attacker in the context of the web server user won't have permission to read the key file or decrypt any credentials in your config file.

It won't protect against physical access, logical access to the system by another administrator level account, or any vulnerabilities that allow a process to be called. It would basically only protect against file reads of the config file directly by the web identity (the user the website itself runs as). If this is not a concern, then there is no need to encrypt secrets at all - the only thing it would really be protecting was casual observation by parties that may see the configuration file.

1

If you are using Java, shouldn't you be using the Java KeyStore?

https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html

This class represents a storage facility for cryptographic keys and certificates.

A KeyStore manages different types of entries. Each type of entry implements the KeyStore.Entry interface. Three basic KeyStore.Entry implementations are provided:

KeyStore.PrivateKeyEntry This type of entry holds a cryptographic PrivateKey, which is optionally stored in a protected format to prevent unauthorized access. It is also accompanied by a certificate chain for the corresponding public key.

Private keys and certificate chains are used by a given entity for self-authentication. Applications for this authentication include software distribution organizations which sign JAR files as part of releasing and/or licensing software.

KeyStore.SecretKeyEntry This type of entry holds a cryptographic SecretKey, which is optionally stored in a protected format to prevent unauthorized access.

KeyStore.TrustedCertificateEntry This type of entry contains a single public key Certificate belonging to another party. It is called a trusted certificate because the keystore owner trusts that the public key in the certificate indeed belongs to the identity identified by the subject (owner) of the certificate.

This type of entry can be used to authenticate other parties.

  • 1
    I was mentionning this as option 3. But the KeyStore needs a password to unlock. How should this password be stored? Answering this question is the same as answering the main question in the first place (in a file, harcoded, ...) – superbob Jan 25 '16 at 7:32
  • Ah, OK. Sorry I'm not a Java expert - in fact I despise it generally for the problems it creates. If you want to avoid EXTRA passwords on the client end, you need to use an OS level keystore that integrates with the OS login or some other login such as an OAuth based cloud login. Or use 2-factor auth to take the user interaction outside the main OS or use a token. – Julian Knight Jan 25 '16 at 8:32
  • 1
    In my question, I said that "It cannot rely on a OS or distribution specific tool such as GNOME keystore, KDE Wallet or any of these unless Java provides way to handle them (but I doubt it).". Also, in the examples I gave (client_credentials grant, and database connection) user interaction is not involved, so it would be hard to rely on it. I'm not sure that a token would be enough, because one way or the other, there would probably be a secret to store somewhere to accept this token, right (I'm not an expert here)? That's all this question is about: how to store the secret? Thx anyway :) – superbob Jan 25 '16 at 10:09
  • 1
    Yes, realise that but it needed mentioning because your request is somewhat unreasonable - you need user interaction somewhere. With a token, the "secret" is in the token but even that is normally combined with a PIN at least. – Julian Knight Jan 25 '16 at 12:21
  • It is right, there are user interactions, but they are "far". The main program works asynchronously, the end user may ask for things to be done later without waiting for an imediate response. In that case, the user token would need to be stored until the end of all the related asynchronous operations. Moreover when the secrets are infrastructure passwords (e.g: a database connection password), there would need to be a way to "link" this password to each present and future users which could be really painful (key derivation at multiple levels, etc...). – superbob Jan 25 '16 at 14:23

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.