Educause Security Discussion mailing list archives

Re: Storing encryption strings - best practice?


From: Alan Amesbury <amesbury () OITSEC UMN EDU>
Date: Tue, 7 Mar 2006 16:53:32 -0600

Mercer, Susan wrote:

[snip - overview of application]
The encryption algorithm requires a string to "seed" it.  That string is
used to encrypt the data to store it, and also to unencrypt it to
display it (SSN only...passwords are 1-way encrypted).  Our question is:
where should we store this string?

I'm not sure I understand what you mean by "seed."  The term, used in
the context of "seeding" a random number generator (which is then used
for cryptographic operations) usually isn't stored; typically it's made
up on-the-fly through the hashing of data from high entropy sources
(network and disk interrupts, keystrokes, and other system events).  The
random number generator is then used to do things like pick session
keys, salts for password hashes, etc.

If you actually mean "key," then I highly recommend that you don't store
it anywhere on the system, if at all possible.  However, since you don't
list that as a choice:

Our choices are:

*         In a configuration file (least secure should someone get
access to the machine)

I'd say this is probably the best option available.  In theory you're
not storing the configuration with the data being encrypted.

*         In the code itself (more secure...but could be
reverse-engineered if someone wanted it)

This is by far the *worst* option.  If it's compiled into your code,
then it's impossible to change if it ever becomes compromised.  This
sort of thing has been played out many, many times over; you really DO
NOT want to do this.

*         In the database (somewhat secure...but could be accessed if
someone gets access to the database)

I strongly dislike this because it's generally considered bad crypto
discipline to store your key with the data it protects.  In penetration
tests and forensic analysis I've used this sort of thing to great
advantage against my opponents.

We are also thinking about obfuscation...putting it somewhere with a
filename/tablename/fieldname that seems quite innocuous and somewhere
you would never think to look for this type of information.

Obfuscation can't hurt, but you should consider it the *least* effective
method of protecting your data.

I'm not sure if you've realized this or not, but simply hashing your
SSNs isn't going to slow down their recovery much, should your database
get stolen (or otherwise accessed by an attacker).  Assuming you're
hashing the SSNs, you still need to keep in mind that, without a salt,
it's (probably) going to be trivial to brute-force the hash.  Consider
this brief attack analysis.

An attacker will already know this information:

        * Not all numbers need to be tried (see
          http://www.socialsecurity.gov/employer/stateweb.htm for
          list of valid three-digit prefixes)

        * An SSN consists of nine digits.

        * The format of an SSN is usually represented as
          nnnnnnnnn or nnn-nn-nnnn.


The worst-case scenario is that an attacker will simply try *all*
possibilities, i.e., all possible nine digit numbers in the two commonly
used formats shown above, or two billion possibilities (10^9*2).
OpenSSL's built-in benchmark ('openssl speed md5') says my workstation
can do a bit over 2M MD5s in a bit under three seconds, or two billion
in a bit under an hour (benchmark output below).

While variants on the MD5 hash exist that make use of salts (e.g., the
one used in FreeBSD, Linux, and others for hashing passwords), the
narrowly defined search space of the database still makes brute-force
attacks against SSNs pretty easy.  The same machine, when running John
the Ripper (a common, well-known, and *fast* password cracker), is
capable of only around 7000 hashes/sec.  However, that's still fast
enough to brute-force two billion possibilities in under four days.

Anyway, that's a couple things to keep in mind when choosing defenses to
delay/deter attack.....


--
Alan Amesbury
University of Minnesota




% openssl speed md5
To get the most accurate results, try to run this
program when this computer is idle.
Doing md5 for 3s on 16 size blocks: 2066350 md5's in 2.96s
Doing md5 for 3s on 64 size blocks: 2021046 md5's in 2.99s
Doing md5 for 3s on 256 size blocks: 1376448 md5's in 2.97s
Doing md5 for 3s on 1024 size blocks: 692847 md5's in 2.98s
Doing md5 for 3s on 8192 size blocks: 114744 md5's in 2.99s
OpenSSL 0.9.7e-p1 25 Oct 2004
built on: Tue Feb 14 14:16:27 CST 2006
options:bn(64,32) md2(int) rc4(idx,int) des(ptr,risc1,16,long)
aes(partial) blowfish(idx)
compiler: cc
available timing options: USE_TOD HZ=128 [sysconf value]
timing function used: getrusage
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192
bytes
md5              11165.57k    43275.12k   118555.95k   237761.90k
314750.92k

Current thread: