Secure Password Storage

If you are using SSL with your Web Server 6.1, your server has one or more private keys. These keys are kept in the NSS database and they are encrypted. In order for the server to read its own keys it will need to decrypt this store, for which it will need the password to the NSS database. When the server is started, if SSL is needed, the server prompts for the password it needs, which is then used to unlock the NSS database.

Often, this is inconvenient. After all, servers need to start unattended. In that case, the only solution is to store the required password somewhere so the server can automatically get it during startup.

This is handled in 6.1 by storing the password in a file called password.conf in the config directory. This file is owned and readable only by the web server process, so it is not possible for other users on the system to get at it.

However, the operating system filesystem permissions are sometimes seen as being too weak. An attacker who manages to crack or bypass the file protections will be able to obtain the cleartext password, which is a problem.

Let’s see how we can improve this situation.

I’ll make a small modification to the start script so it obtains the password by invoking an executable (I’ll call it wsgetpwd) instead of prompting interactively:

99c99,101
<               ./$PRODUCT_BIN -r $SERVER_ROOT -d $INSTANCE_CONFIG_DIR -n $IN
STANCE_NAME $@
---
>               PWD=`wsgetpwd $INSTANCE_CONFIG_DIR`
>               echo $PWD | ./$PRODUCT_BIN -r $SERVER_ROOT -d $INSTANCE_CONFIG_D
IR -n $INSTANCE_NAME $@

Important Note: The content of the start script is not a public interface. This means any changes to it are unsupported and it also means you cannot expect any such changes to continue working after a service pack or version upgrade. You’ve been warned. No production servers were harmed in the writing of this article.

Ok, with this tiny bit of infrastructure in place we can experiment with various implementations of wsgetpwd until we find something superior to keeping the cleartext password in password.conf.

Let’s start simple to see if it works. I create a file called password in the instance config directory to contain the password and implemented wsgetpwd to simply print it out:

% rm -f config/password.conf
% echo password > config/password
% chmod 600 config/password
% cat ../bin/https/bin/wsgetpwd
cat $1/password

Starting the server shows that it works fine. So far so good. We haven’t accomplished much yet though – if our attacker can bypass the filesystem permissions on password.conf, they might also bypass the permissions on the new password file.

So, let’s improve wsgetpwd. This time I’ll obfuscate the password using base64 (btoa/atob are small utils from NSS which do this encoding) so it is no longer human-readable.

% echo password | btoa > config/password
% cat config/password
cGFzc3dvcmQK
% cat ../bin/https/bin/wsgetpwd
cat $1/password | atob

Now, even if an attacker manages to read the password file, they’ll only get “cGFzc3dvcmQK” which doesn’t really do them much good (unless they know about atob or base64, but how likely is that?)

Nonetheless, it’s been suggested that the password will be safer if it is encrypted with a proper encryption algorithm. Encryption is really hard to break, so that will certainly improve the security even further. I’ll use encrypt(1).

Some prep work is needed first. I’ll use AES encryption and I’ll use /dev/random to obtain bits for the key and then I’ll encrypt the password with that key into the password file. Finally, I reimplement wsgetpwd to decrypt the password at runtime.

% dd if=/dev/random of=config/encrypt.key bs=1 count=16
% chmod 600 config/encrypt.key
% echo password | encrypt -a aes -k config/encrypt.key > config/password
% cat config/password | od -x
0000000 0000 0100 0000 e803 058c 6f08 5b48 c607
0000020 3517 7fc5 65ce c64e 95ff 576d ee95 4cb4
0000040 e990 5834 df32 9042 df90 9937 47f4 a464
0000060 2720 f8db ad0a d089
0000070
% cat ../bin/https/bin/wsgetpwd
decrypt -a aes -k $1/encrypt.key -i $1/password.aes

Start the server and.. it works! The password is now encrypted with AES on disk and the server is still able to start automatically. When our attackers crack the filesystem permissions on the password file all they will get is the encrypted bits (shown by the od -x output above) – the cleartext password remains secure.

If you’ve read this far, one final thought: Should I file this article under “Web Server Security” or under “Light Comedy”?