Tuesday, June 16, 2009

One Way Hash - Credit Card storage?

In ecommerce it is very common for us to come across situations to deal with credit cards and when it must be stored, then the security of it. Recently, was dealing with design of a visa service (confidential) and had to address the storage of CC numbers under PCI guidelines. I would recommend to take a look at this interesting blog - PCI Integrity Corp. There's a lot of documentation, suggestions, issues, complaints, models... e.t.c one can go through. I would like to give my version here and why I chose to do so.

My model:
1. Use one way hash
2. Use salt (even if it's hard coded)
3. Do a minimum of 100 iterations
4. Use 512 encryption (on XP developer machine use SHA512Managed but on servers 2003 and above SHA512CryptoServiceProvider could be used as well)

Let me explain why I chose to use salt that is hard coded in the code. Yes, developers can see it and what's the point in not addressing the separation of responsibilities here... well, the rational is to prevent the external threats. Hashing without salt itself is acceptable under PCI and adding an internally known salt won't make it any worse but rather make it more secured for external threats. Enough said, take a look at a prototype code that can work for this model:


using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;

/// <summary>
/// HashManager
/// </summary>
public sealed class HashManager
{
private static readonly string _salt = "BD4332CF-EE54-4BB1-BCCF-BD7A0F0C7F1F";
private static readonly int _hashIterationsMax = 100;

private HashManager() { }

#region Public Methods
/// <summary>
/// Gets the salted hash value with predetermined iterations.
/// </summary>
/// <param name="unhashedData"></param>
/// <returns></returns>
public static string GetSaltedHash(string unhashedData)
{
string hashData = unhashedData;
for (int hashLimit = 0; hashLimit < _hashIterationsMax; hashLimit++)
hashData = GetHash(_salt + hashData);
return hashData;
}

/// <summary>
/// Verifies the hash
/// </summary>
/// <param name="unhashedData"></param>
/// <param name="hashedData"></param>
/// <returns></returns>
public static bool VerifyHash(string unhashedData, string hashedData)
{
string hashData = GetSaltedHash(unhashedData);
return hashedData.Equals(hashData);
}

#endregion Public Methods

#region Private Methods
/// <summary>
/// Gets the hash value of the data using SHA512Managed
/// </summary>
/// <param name="unhashedData"></param>
/// <returns></returns>
private static string GetHash(string unhashedData)
{
byte[] hashData = Encoding.UTF8.GetBytes(unhashedData);
// on server 2003 or higher, can use SHA512CryptoServiceProvider
SHA512Managed sha512Managed = new SHA512Managed();
hashData = sha512Managed.ComputeHash(hashData);
sha512Managed.Clear();
return Convert.ToBase64String(hashData);
}

#endregion Private Methods

}

Love coding!

2 comments:

Anonymous said...

I don't understand what the purpose of storing hashed credit card numbers would be. They're irretrievable; even you can't get them back out of the database once they've been stored as hashes. You might as well not store them at all.

Kishore Gorjala said...

Sure, I agree it's not a common requirement and this post is about the idea first of all and could be used to store passwords and other such.

However, a requirement I came across, that triggered me to post this, was about a background credit card benefits processing and being able to cache some benefits associated with a card for some extended period. In that case it's not built for getting the card number back, but rather when a card number is known to find the benefits from the cache.

Hope this helps,

Love coding!