Encrypting cc_number in uc_payment_credit table.

Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

This is of slight concern and I think it should be a high priority.

Right now credit card numbers are being stored (if chosen to be saved in the db, of course) as unfiltered numbers. I would recommend using an md5 or other type of encrypt() using md5/SALT and a random string, configurable only from within a settings file, and storing the data as a Binary BLOB. This ensures the most protection in case of unwarranted access to payment history / customer card information.

And of course it's easy enough to decrypt back and forth, as long as you have the key, so that during processing between admin and payment gateways you can still send out the actual card number.

I have code I can recycle from our current shopping cart system that uses a pretty solid method of encryption (and it's easy enough to change the encryption key). This is something I would need pretty much ASAP (and I can't imagine anyone else who wouldn't want this). Let me know your thoughts.

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

Just FYI, checking the "do not store card numbers, even at checkout" works great for us at the moment. Since it only stores the last four digits of a card, I'm still able to process the payment as well as issue refunds to users when they need it. But I can't remember if that's the default, or if the default is to store the entire number. (It's been a while since I installed UC).

In any case I think if someone is storing the entire number there should be encryption in place; you may even consider encrypting anything that gets stored in the cc_number column.

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 5
Joined: 08/12/2007

The level of liability that a store owner opens themselves up to by storing credit card numbers is huge. Of course storing CC numbers and passwords anywhere that are not encrypted is a huge security hole.

To be frank, I strongly recommend against this practice, especially if you are using open source code. You can easily issue credits through payment gateway providers such as Authorize.net, Versign, and TransAction Central....

Posts: 4368
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

I'd be happy to look over your previous encryption code. Like Doug pointed out, the liability is huge, but folks that are storing whole numbers are probably doing something like offline processing and should be aware that they're not necessarily doing the best thing. I can't use md5() or crypt(), though, because that's just a one way hash... no way to decrypt it. I thought about doing some obfuscation, but I didn't waste my time since it's not real security and anyone could reverse engineer it since Ubercart is open source.

Posts: 2008
Joined: 08/07/2007
AdministratoreLiTe!

If someone gets complete access to your database, you're pretty much hosed. Any encryption method that allows decryption can eventually be cracked by someone with enough time and determination. That's probably a lot easier than getting into the database through all that security, anyway.

The best practice is to really not store the entire number at all.

Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

Okay, I threw the crypt / decrypt files onto my server. Check them out here:

http://shop.rifftrax.com/crypt.php
http://shop.rifftrax.com/decrypt.php

For testing values I'm using a bogus card number: 4111222233331234
This value is stored in the first line of crypt.php for the time being.

You can refresh the page and find random hashes for the card number (or whatever number you like stored in the $cc_num variable - hard coded for this test) and then plug the Base64 encode of the md5-block-cipher style encoded value (all 88 characters including the trailing periods) into the form field, and hit decrypt. Voila! You're back to the bogus card number.

(The secret is the passphrase that needs to be stored in a central location somewhere).

I can PM you the code if you want, I'm not sure I want to post it in the open forum.

Erik

UPDATE: Added the last four digits version of the card. You can paste that encoded value into the decryption form and it will return the correct value as well.

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 1
Joined: 10/11/2007

CC Encryption is definitely an issue that needs to be addressed. While Lyle is technically right that any two-way encryption scheme can be decrypted if someone has the resources, it is HIGHLY unlikely if the encryption is done correctly.

Lyle is also correct that the best policy is not to store the CC numbers at all, but that is not always an option. For systems that require recurring billing, keeping the CCs in integral. So a solution needs to be found. Additionally, Ubercart (currently) stores the CC in the database between transaction stages. So if someone abandons their cart (even if they have the option checked to not store CC numbers after the transaction), the CC remains in the DB. That CC should be encrypted, even if it is only temp storage, because you never know when a user will abandon the cart, or close the browser for another reason, and if the CC will be in the DB it will be stuck and potentially exposed.

Even if MD5 were a two-way encryption methodology, it is not strong enough for CC numbers. Too may Rainbow table projects exist for it. Something stronger is needed.

Mcrypt is likely the best solution. It is relatively easy to implement and it seems solid.

Using MCypyt with AES/Rijndael (128, 192, or 256) should be MORE THAN adequate protection for storing CC numbers in an Ubercart DB table. By using MCrypt with AES/Rijndael on a credit card number with a Random Salt concantenated and then a site salt and an administratively set cipher key, those numbers would be encrypted and obfuscated beyond reasonable means for decryption.

So what I am envisioning is a DB entry into cc_num(blob) built like this

$random_salt = md5(uniqid(rand(), true));
$text = $site_salt . $cc_num . $random_salt;
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);

Compnents of $text:
1. $site_salt - Would be a salt that the administrator would create and store in an offline file (perhaps settings.php)

2. $cc_num is the plain test CC number

3. $random_salt is a random unique id generated on the fly for each user and stored in the DB, perhaps in a salt table that is related either to the user table, where each user would have their own salt, or to the uc_payment_credit table where each CC payment would have its own salt.

This way the "bad guys" would need more than just access to the uc_payment_credit table to be able to do anything with the encrypted credit card numbers. They could begin trying to brute force attack those numbers, but in 100+ years, when they finally succeeded, would anyone care?

To crack the encryption, they would need access to the database, AND the key stored in the protected system file (Which should be possibly be excluded from off-site backups).

The random_salt is merely there to protect against brute force attacks against the credit card table. If the entire db is compromised, the random salt only helps with obfuscation.

Now, I am not an absolute expert on this stuff. I am also not a PHP developer or Drupal Develper by training, however I am working on it. I am a ColdFusion developer and I have been researching CC and Password encryption for some projects. I am a Drupal user and I am currently setting up an Ubercart site for a friend, and when I saw that cc_num was being stored in plain text, I had to say something. If there are glaring errors in my logic, I would like to have them pointed out to me.

AES/Rijndael 192 and 256 encryption are an acceptable standard for US Government Secret and Top-Secret classified information protection link, so they are probably good enough for an online store.

Posts: 27
Joined: 09/22/2007

This sounds like a good overall strategy, but I would suggest the option be included of not storing the private key on the webserver at all. In that cage, it could be stored on the site admin's local PC and copied and pasted into a textbox on the page for viewing order details.

Or perhaps, having the order page show an encrypted credit card number that the user has to download to decrypt using local decryption software.

Posts: 10
Joined: 11/08/2007

I use the "Don't Save CC Numbers, Even at Checkout" setting as well, but it still doesn't solve the issue of clearing out full CC numbers if an order is not successfully completed.

Other than having the Cron chop up left-behind credit cards every night, is there a way to prevent them from being stored?

--

Chris Albrecht

Posts: 1
Joined: 11/30/2007

Another solution I ran across: cron job to remove sensitive customer info from the exposed server and download to a secure, essentially offline server.

Obvious disadvantage: you have to do all of your order processing from the back end.

However, if your online store is only one facet of your operation, you might have to populate an offline order system anyhow.

Best regards,
Alan

Posts: 13
Joined: 11/09/2007

Hi Chris,

We are also using the "Don't Save CC Numbers" option, and your statement about the problem of clearing out CC numbers if an order fails grabbed our attention. Under what circumstances might this occur? We are wondering if we would have to write some kind of custom script to comb the database and clear out any such lingering CC numbers, but we're not sure what sequence of events would lead to such an unintended CC number storage.

Thanks,
Chuck & Kim

Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

I talked about this here: http://www.ubercart.org/issue/1828/when_declined_entire_credit_card_numb...

It happens when an order is declined, and the user abandons the cart; or if something happens that causes the browser to crash and keeps the order "in checkout".. that will cause the full card number to not be processed along with Cron. (Might be an easy fix to include numbers from all orders).

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 4368
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

I ended up grabbing Tony Marston's encryption class for use in core (it's GPL and has no external dependencies). As I was reviewing the credit card settings to figure out where to put the encryption settings, I realized that the last few fields can be used to take care of the sort of "leftover" cc numbers described in the above 2 posts. Just set the order status to wipe to "In checkout" and put the time limit to 1 hour. Is there a problem with this?

Also, as we move forward, I'm considering the fact that including configurable messages in settings forms has led to problems with multilingual sites. Does anyone alter the failed CC message or can I just make that a default and remove that textarea? It would still be editable through translations/localization.

Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

No problem with the In Checkout setting; I just set it to use that, and will check on it periodically to see if it helps with the stragglers.

As far as the failed CC message, I think keeping it flexible is good. We will be changing it from "email support" to "click here to visit our support center" that'll be an Ajax FAQ module of sorts. Since I'm not sure what your plans are for it, as long as there is some form of customization (a way to actively guide the customers in case of a CC authorization failure), do what you must!

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 4368
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

Alright... moved all the CC settings around, made them more logical with better help text, and I documented all the settings in the user guide. Please review that compared with the attached screenshot and let me know if it makes sense. Eye-wink Once encryption is complete I'll make a separate post about it instructing users on how to best protect themselves and their customers from fraud.

AttachmentSize
new_cc_settings.jpg235.66 KB
Posts: 1139
Joined: 08/14/2007
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.

Looks good to me. Once you've committed I'll be glad to help test in the sandbox - will hold off for the live site until then Smiling

--

"Pain don't hurt." - Dalton

Mike Nelson's RiffTrax! www.rifftrax.com

Posts: 4368
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

I know tP saw the other post, but in case anyone else didn't who's tracking this thread... CC encryption has been committed to core with a few functions for existing sites to encrypt any existing data as well. This change also totally restructured the CC settings form to make it a little more logical. Laughing out loud

http://www.ubercart.org/forum/announcements/2762/cc_encryption_bazaar_ne...