PDA

View Full Version : Encryption for credit card numbers


Lloyd
01-01-2009, 06:22 PM
Hello Rimuhosting,

A while back I submitted a support ticket containing suggestions for public key encryption for credit card numbers stored by the Rimuhosting billing sytem. Carl answered my ticket, saying he would forward it to your developers, and said he thought some sort of encryption was already being used. I'd like to know if that is the case. If it is, the fact should be publicized.

Anyway, I'm publishing the suggestions and code examples from my support ticket here for all those who are interested in storing credit card numbers securely, for "scheduled," regular charges. (My suggestion for the situation when charges are not regular is to not store the credit card numbers at all.) Following is the text of my support ticket orginally submitted Dec. 28.
----------------------------------------------------------------

Subject: suggestion for secure handling of credit card numbers

Message:

Hello Friends (This is, I think, for Peter Bryant):
I have a suggestion for you to handle credit card numbers more securely. It works for companies that do regular credit card charges, which I believe is the case for Rimuhosting. I implemented this on a website and it works beautifully. (If you already do something similar, pardon me, but in that case you should brag about it on your website!)

Essentially, the idea is to store the customer credit card numbers only in GPG-encrypted form, one file per card number. This assumes you assign each customer some unique customer number. In my implementation, I combine the customer number with the last 4 digits of the credit card number to obtain the filename for saving the encrypted credit card number. This would allow mulitiple credit card numbers per customer, although I don't current make use of that.

The public key required to encrypt the card numbers is of course stored on the server. The *secret* key is stored there too, but it is "locked" with a password. Thus, customers can update their credit card number, or add a new number, at any time, through the web intereface. (Actually, if a customer "edits" the credit card number, the old GPG file is simply deleted, and a new one is stored in its place.)

When it is time to charge customers using the GPG-encrypted credit card numbers, a Rimuhosting administrator logs in to a secure (ssl over http) web interface, and enters the GPG password required to unlock the private key used for decryption. All the customers with a pending hosting payment who have elected to have their credit card info stored on the server are processed at once. The encrypted credit card files are opened only during the instant that the credit card payments are processed, and only in memory. At no time are the card numbers stored in cleartext form on disk, unless the server is swapping, which is highly unlikely.

As a result of this, even if the server is *completely* compromised, with some "bad guy" having root access, it is highly unlikely that the customer credit card numbers will ever get into the wrong hands! This scheme also has the advantage that not all your staff has to have access to credit card numbers, even though they have root access to the server where they are stored.

Following are some code snippets in perl taken directly from my working scripts. The first code snippet detects a new credit card number, validates it, and puts it into the variable "$newcardnum" for later processing. "$fieldname" is the form fieldname and "$value" is the value.:

if ($fieldname eq "TarjetaNumero") {
$value =~ s/\s//g;
if ($record[29] ne $value) {
use Business::CreditCard;
# validate card number
if ($value) {
if (!validate($value)) {
dnsdienice("Incorrect Credit card Number.");
}
$newcardnum=$value;
$value="*****" . substr($value,-4);
} else {
$newcardnum= "NONE";
}
}
}

The following code snippet does the GPG encryption when according to the value of $newcardnum. @record contains the original values from the database:
if ($newcardnum) {
if (!unlink("$fileroot/cc/" . "${custid}_" . substr(fieldval ($oldrecordstr,29),-4) . ".gpg")) {
# print "warning, old gpg file not deleted.<br>";
}
if ($newcardnum ne "NONE") {
my $gpgfilename= "${custid}_" . substr($newcardnum,-4) . ".gpg";
my ($temphand,$tempname) = maketemp();
if ($temphand) {
print $temphand "$newcardnum\n";
close $temphand;
if (system "/usr/bin/gpg", "-a", "-z", "0", "-r", 'lloyd', "-o", "$fileroot/cc/$gpgfilename", "-e", $tempname) {
unlink $tempname;
close $handle;
sendmyemail ('lloyd@dnscostarica.com','lloyd@crnatural.net',"",'ERROR',"","Bad trouble at " . __FILE__ . " line " . __LINE__,0,1);
dnsdienice("CARD NUMBER COULD NOT BE ENCRYPTED");
}
unlink $tempname;
}
}
}

If you have any questions about this, I'd be glad to answer, and help in any way I can.

Best regards,
Lloyd