Les,

Thanks for your ideas, as always! So for password hashing, would you suggest
using something like this?

ByteSource appSalt = new SimpleByteSource(APPLICATION_SALT);
ByteSource userSalt = (new SecureRandomNumberGenerator()).nextBytes();
ByteSource salt = new SimpleByteSource(ArrayUtils.addAll(appSalt.getBytes(),
userSalt.getBytes()));
member.setPassword(new Sha512Hash(dto.getPassword(), salt.getBytes(),
SALT_ITERATIONS).toBase64());
member.setSalt(userSalt.toBase64());

Note that this uses your suggestion to have both an application salt and a
user salt, but only the user salt gets saved to the database. That way a
database compromise would still not give the attacker the full salt value.

Any suggestions on migrating existing simple Sha256Hash() passwords without
interrupting user logins? Perhaps have Member.password and
Member.oldPassword properties. If Member.password is NULL, then test using
the old method and update Member.password with data if old method is
successful. If Member.password exists, then test using new method as user
password has been upgraded already. Or do you have a better idea?

In regards to encrypting sensitive data, I don't yet want to get into the
complexities of rolling keys, although it sounds like a really great idea.
I'm only wanting to make sure my application is PCI compliant, it doesn't
need to provide military grade security. If I choose to not install the JCE
unlimited strength policy files, I will be limited to using AES-128 at the
maximum, right?

When reading the Javadocs, it looks like the IV is prepended to the
encrypted data and stored with it. It also looks like they are comprised of
purely random data. I'm curious what your thoughts are in regards to
including an entity ID into the IV, as suggested in Method 6 in the
following link. To me it really only sounds useful if you're trying to
conserve space in the DB.
http://www.di-mgt.com.au/cryptoCreditcard.html

Assuming I'm not going for rolling keys, would you suggest having one
application-wide key that is used to encrypt and decrypt any sensitive
values? Or would it be better to have different keys for different purposes
(one for credit cards, another for SSNs, etc.)?

Shiro's certainly made it dead simple to work with crypto. Is there anything
wrong with the following implementation, or anything you would do to improve
the way I'm using Shiro's crypto (note I haven't tested the following code
yet)?

/**
 * Encrypt a credit card using the provided key. A key can be generated
with
 * a command like this:
 *
 *  AesCipherService cipherService = new AesCipherService();
 *  Key appKey = cipherService.generateNewKey();
 *
 */
public static String[] encryptCreditCard(String card, Key appKey) {
Validate.isTrue(card != null, "Card number must exist");

// Verify card is between 13 and 19 characters long
int length = card.length();
 Validate.isTrue(length > 12 && length < 20, "Card number has invalid
length");

 String[] result = new String[3];
 // Store the first 2 digits and last 4 digits of the card unencrypted
 result[0] = card.substring(0,1);
result[2] = card.substring(length - 5, length-1);
 String cardBody = card.substring(2,length-6);
 AesCipherService cipherService = new AesCipherService();
// cipherService.setKeySize(256);

// encrypt the middle part of the card number
 ByteSource encrypted = cipherService.encrypt(cardBody.getBytes(),
appKey.getEncoded());
 result[1] = encrypted.toBase64();
 return result;
}
public static String decryptCreditCard(String[] encryptedCard, Key appKey) {
Validate.isTrue(encryptedCard != null && encryptedCard.length == 3, "Invalid
card");

AesCipherService cipherService = new AesCipherService();
// cipherService.setKeySize(256);

// decrypt the middle part of the card number
ByteSource decrypted = cipherService.decrypt(encryptedCard[1].getBytes(),
appKey.getEncoded());
 // combine plain text parts with decrypted part to form full card number
 return encryptedCard[0] + decrypted.toString() + encryptedCard[2];
}

Thanks again for your assistance!
Tauren








On Fri, Apr 1, 2011 at 1:19 AM, Les Hazlewood <[email protected]> wrote:

> Hi Tauren,
>
> Please see inline:
>
> > looked for documentation and samples, but haven't really found many
> > resources on using Shiro's cryptography features.
>
> Yep, this is the biggest remaining hole in our documentation.  We've
> worked hard on filling in the gaps thus far - but that needs to be
> flushed out.
>
> > I have a feeling I could be using the Cryptography features much more
> > effectively. For instance, when a new user signs up, I do the following:
> >     member.setPassword(new Sha256Hash(dto.getPassword()).toHex());
>
> You absolutely want to be using a salt when hashing passwords, which
> will make rainbow attacks virtually impossible.  I highly recommend
> multiple iterations (built in to Shiro's hash concept from inception).
>  See my link [1] below for more.
>
> > I require that all passwords are at least 8 characters long and include
> > characters from at least three of the following four categories:
> lowercase
> > letters, uppercase letters, numbers, and symbols. After reading the
> > following article, I'm worried about just how easy it would be to crack
> > these password hashes:
> >     http://codahale.com/how-to-safely-store-a-password/
> > I seem to recall some talk of performing multiple hashes on things to
> secure
> > them better? Any links to this resource?
>
> The article is mostly correct, but unfortunately the author delivered
> it with a bit of sensationalism and FUD.  His points are true under a
> really big assumption:
>
> That the saved passwords and password salts are easily accessible to
> the attacker.  This means that the attacker has broken through your
> firewalls and other network security layers, through or around your
> app (hopefully secured by Shiro!) and database connection, etc and has
> a ton of computing power ready to churn all of your data.  While this
> is a real possibility for security unconscious organizations, it is
> managed fairly well by most competent organizations.  IMO the biggest
> risk for this type of attack is not computational but social (e.g.
> social engineering - getting access to a developer or system engineer
> password when that person has the keys to the kingdom).
>
> Basically, at the end of the day, if you use a sufficiently powerful
> hash algorithm (SHA-256 or SHA-512), use a salt, _and_ use a large
> number of hash iterations, you'll be doing what that article
> advocates.  The key is not that bcrypt is some super fancy hashing
> algorithm, but that it is slow.  Speed is the key - the slower the
> hash operation, the longer it takes the attacker to thwart a system.
> While it doesn't sound like a big deal, it provides orders of
> magnitude more protection if a compute cluster is trying to thrash
> your data concurrently.
>
> This is why Shiro's Hash concept has had the 'iterations' property
> since it was created - using a Shiro Hash (with a salt and
> iterations), you'd be doing exactly as that blog advocates.
>
> > Also, I have some sensitive data (credit card, SSN, etc.) that I want to
> > encrypt before storing in the database.
>
> Shiro's CipherService will be a huge help in this case.  Using the JDK
> default block cipher (AES, Blowfish, etc) config is just plain
> insecure - they don't use things like InitializationVectors by
> default.  The CipherService implementations will do a lot of this
> stuff for you out of the box.
>
> The safest thing to do in this case to the best of my knowledge is to
> have a rolling key mechanism:  use AES 256 encryption (requires the
> JCE Unlimited Strength policy files to be installed) using Shiro's
> AesCipherService.  Every n period of time (hours, days, etc),
> un-encrypt what you currently have with the existing AES 256 key,
> generate a new AES 256 key, and re-encrypt the data and save it.  This
> ensures that there is a known (and limited) window of time for which
> the key is valid.
>
> Rolling keys reduce the ability of any previous key accessors (even
> employees) to access the data at a future time.  I know of a more than
> a few companies that require rolling key policies for sensitive data,
> and it is especially smart to do with super sensitive information (HR
> records, credit cards, etc).  I would think a typical web app store
> front could have n equal to 1 or 2 days or even up to a week.  However
> if the number of records is low (only a few million or less), then the
> computation is cheap/fast and having n equal 1 day (or even less) is a
> pretty good practice IMO.
>
> > I'm considering adding BouncyCastle
> > to my application, but perhaps I can use existing Shiro features.
>
> Yep, BouncyCastle is a complementing mechanism to Shiro - not a
> competitor.  Because Shiro sits on top of standard Java cryptography
> classes, and BouncyCastle can be a JCE provider, Shiro will
> effectively 'sit on top of' BouncyCastle quite nicely.  This isn't
> really necessary in my opinion though - AES 256-bit encryption with
> Initialization Vectors is approaching military-grade encryption
> (without the physical security part since Shiro is all software based)
> and is already available in the JDK by default.  You just need to
> ensure you've installed the JCE Unlimited Strength policy files.
>
> Here are some links that might be of help:
>
> [1] A previous discussion of password hashing techniques/reasoning:
> http://www.mail-archive.com/[email protected]/msg00256.html
> [2] A presentation I gave to the San Francisco JUG on Shiro:
> http://www.youtube.com/watch?v=5ZepGFzYHpE  Jump to the 45:52 mark for
> the Cryptography part of the presentation.
>
> HTH!
>
> Best,
>
> --
> Les Hazlewood
> Founder, Katasoft, Inc.
> Application Security Products & Professional Apache Shiro Support and
> Training:
> http://www.katasoft.com
>

Reply via email to