Jump to content

Archived

This topic is now archived and is closed to further replies.

nfrobertson

Encrypting Credit Card Number (cc_number) in OSC

Recommended Posts

Ok, I searched around and read several threads about credit cards being stored in plain text in the orders table. I wanted a quick way to encrypt the cc_number in the orders table so it wasn't out there in plain text. PHP supports the mcrypt library and I was able to get it working on my windows test box so this seems like a good path to take.

 

I understand there's an entire body of thought out there on the validity of even storing the cc_number. I'm not intending to start another practicality debate thread but rather to help anyone who may have this same need. I also know that there are shorter/simpler ways to use mcrypt but what I found was that without generating the IV, PHP logged warnings. The code at the end here gets around that *and* you don't have to add any columns to the db to store the IV.

 

What the steps below basically do is encrypt the cc_number using a key you put in your configure.php. It does this right before stuffing it into the orders table. Then, in the admin area, when it pulls the order out of the database for you to view, it decrypts it on the fly.

 

This is a version 0.1 effort. This, like other support and contribs falls into: Use at your own risk.

 

 

STEP1

First verify that your PHP has mcrypt installed. Check the output of <?php phpinfo(); ?> for the string mcrypt. Look here for more info: http://www.php.net/mcrypt

 

STEP2

Modify the orders table and set the cc_number column type to TINYTEXT. Varchar won't work as trailing spaces are removed. Here is example SQL that MySQL Admin generated for doing this:

 

ALTER TABLE `oscommerce`.`orders` MODIFY COLUMN `cc_number` TINYTEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci;

 

STEP3

catalog/checkout_process.php

 

After (approximately line #13)

include('includes/application_top.php');

Add

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

 

After (approx line #56)

$order_totals = $order_total_modules->process();

Add

$cc_number_crypt = base64_encode(cc_encrypt($order->info['cc_number'], CCKEY));

 

Find (approx line #93)

'cc_number' => $order->info['cc_number'],

Replace with

'cc_number' => $cc_number_crypt,

 

STEP4

catalog/admin/include/classes/order.php

 

After (approx line #28)

$order = tep_db_fetch_array($order_query);

Add

$cc_number_decrypt = cc_decrypt(base64_decode($order['cc_number']), CCKEY);

 

Find (approx line #43)

'cc_number' => $order['cc_expires'],

Replace with

'cc_number' => $cc_number_decrypt,

 

STEP5

catalog/admin/orders.php

 

After (approximately line #13)

include('includes/application_top.php');

Add

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

 

STEP 6

catalog/includes/configure.php

 

Add (before the final ?> at the end of the file)

define('CCKEY', "YOUR_SECRET_KEY_HERE");

 

catalog/admin/includes/configure.php

 

Add (before the final ?> at the end of the file)

define('CCKEY', "YOUR_SECRET_KEY_HERE");

 

STEP7

The following code is based entirely on user provided example code found at php.net at this page

http://www.php.net/manual/en/function.mcry...te-iv.php#40434

Author: robert at peakepro dot com

I changed the encrypting alg. used and added the trigger error in case something doesn't work. I've tested this on my windows test server and it works.

 

Install the following code into cc_crypt.php in both these locations:

 

catalog/includes/functions/cc_crypt.php

catalog/admin/includes/functions/cc_crypt.php

 

<?PHP
function cc_encrypt($string,$key) {
  srand((double) microtime() * 1000000); //for sake of MCRYPT_RAND
  $key = md5($key); //to improve variance
 /* Open module, and create IV */
 $td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '','cfb', '');
 $key = substr($key, 0, mcrypt_enc_get_key_size($td));
 $iv_size = mcrypt_enc_get_iv_size($td);
 $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
 /* Initialize encryption handle */
  if (mcrypt_generic_init($td, $key, $iv) != -1) {

 /* Encrypt data */
 $c_t = mcrypt_generic($td, $string);
 mcrypt_generic_deinit($td);
 mcrypt_module_close($td);
   $c_t = $iv.$c_t;
   return $c_t;
  } else {
  	trigger_error("cc_encrypt: Unable to encrypt input", E_USER_NOTICE);
  }
}

function cc_decrypt($string,$key) {
  $key = md5($key); //to improve variance
 /* Open module, and create IV */
 $td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '','cfb', '');
 $key = substr($key, 0, mcrypt_enc_get_key_size($td));
 $iv_size = mcrypt_enc_get_iv_size($td);
 $iv = substr($string,0,$iv_size);
 $string = substr($string,$iv_size);
 /* Initialize encryption handle */
  if (mcrypt_generic_init($td, $key, $iv) != -1) {

 /* Encrypt data */
 $c_t = mdecrypt_generic($td, $string);
 mcrypt_generic_deinit($td);
 mcrypt_module_close($td);
   return $c_t;
  } else {
trigger_error("cc_decrypt: Unable to decrypt input", E_USER_NOTICE);
  } 
}
?>

Share this post


Link to post
Share on other sites

Looks like STEP3 above also needs to be done to admin/invoice.php and admin/packingslip.php

 

STEP3b

admin/invoice.php

admin/packingslip.php

 

After (approximately line #13)

include('includes/application_top.php');

Add

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

Share this post


Link to post
Share on other sites

Just FYI if you want to check it out to see how they do it.

 

I have the ECHO credit card processing module (ECHO Clearing Corp is my CC processor/marchant acct)

 

It encrypts/decrypts the CC info in the DB table.

 

I also use clean_cc_numbers contribution to clear out the encrypted CC numbers from the DB.


Thanks

 

Mike

Share this post


Link to post
Share on other sites

Thanks Mike. I'll take a look at that too.

 

I was able to integrate the mcrypt stuff above with the Card Zapper contrib so I have something similar setup now.

 

My plan is to encrypt the card into the DB when the order is placed then, after a certain length of time either use Card Zapper (per order) or run a script (mass db update) that does the same thing so my DB shows xxxxxxxxxxxx####. This was explained in the Card Zapper contrib as useful so the customer could identify which card was used for the order. Then after a longer period of time when the # is truly useless I'll run another script to drop it from the database.

Share this post


Link to post
Share on other sites

I'm on the last leg of my first ecommerce store. When I realized it stored the CC#s in an unsecure area, it blew my mind, but thankfully this post just popped up.

 

I like the idea of using mcrypt, and have it available to me, but got lost on step 2, heh. Where's the orders table? it didn't seem to be the the same as orders.php

 

I just installed card zapper, but then realized each card has to be zapped manually. Anyone know of a way to do this automatically? I use Authorize.net, so I don't have any need to be able to eyeball any physical card numbers, though I may need to keep the #s for cretiting back bad charges or something. Like I said

<--- sooper noob

 

Also,

Thanks in advance!

-Tristan

Share this post


Link to post
Share on other sites

I just found out that Authorize.net stores the Credit Card info, so I definately don't have any need to store CC info in my database. Automatic Card Zapper would be awesome if anyone knows how to "make it so"

 

(As an aside, does anyone know how to edit a post. I wanted to just edit my previous post to reflect this, but I can't for the life of me see where that's possible)

Share this post


Link to post
Share on other sites

If all you want to do is "auto-zap" the credit cards so they end up as something like "xxxxxxxxxxxx 1234" in the database, you can do this by only modifying:

 

catalog/checkout_process.php

 

Find (approx line #93)

'cc_number' => $order->info['cc_number'],

Replace with

'cc_number' => "xxxxxxxxxxxx" .  substr($order->info['cc_number'], -4, 4);

 

This is safer than encrypting the card as you're only storing enough to help the customer identify which of their cards they may have used for a purchase. This is what the 'card zapper' contrib (the most recent version) does but, like you mentioned, that's a manual process.

Share this post


Link to post
Share on other sites

There is just one problem with this whole encryption script.

 

If you happen to have your card setup so you can delete the credit card from the database, because of the way this script was written, it requires that there be something in the CC_Number field and you get the following error message:

 

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 0, needed: 32 in /home/edealer/public_html/catalog/admin/includes/functions/cc_crypt.php on line 33

 

Great ideas and wonderful piece of work, however, any ideas how to make this work after the credit card has been deleted from the database??


David McGuffin - Website Developer

New Look Web Design

www.newlookwebdesign.com

---------------------------------------------------------------------

"I never memorize anything I can simply look up. . ." ~Albert Einstien

Share this post


Link to post
Share on other sites

Thanks for the question, and yes, there actually is. I just hadn't gotten back to posting it here. :blush: Since my original posting, I've added support for the Card Zapper contrib. So, now it's actually possible to have the cc_number in one of three different states: encrypted, zapped (xxxxxxxxxxxx1234) or empty/blank. Below is how I've handled this:

 

STEP 4 (modified)

catalog/admin/include/classes/order.php

 

After (approx line #28)

$order = tep_db_fetch_array($order_query);

Add

	  // See if we need to decrypt - if it's already been zapped then no decrypt
  $cc_number_decrypt = $order['cc_number'];
  if(tep_not_null($cc_number_decrypt)) {
	  if(strncmp("xxxxxxxxxxxx", $cc_number_decrypt, 12)) {
		$cc_number_decrypt = cc_decrypt(base64_decode($order['cc_number']), CCKEY);
	  }
  }
  else {
	  $cc_number_decrypt = '';
  }

 

Find (approx line #51)

'cc_number' => $order['cc_expires'],

Replace with

'cc_number' => $cc_number_decrypt,

 

 

I have also discovered a couple more places where the require line needs to be:

 

STEP3c

catalog/account_history_info.php

catalog/admin/zapt.php (for Card Zapper)

print_my_invoice.php (for Fancier Invoice & Packingslip v1.0)

 

catalog/

After (approximately line #13)

include('includes/application_top.php');

Add

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

Share this post


Link to post
Share on other sites

Please note the typo in post#1 STEP4 and post#9 STEP4 (modified)

 

Find (approx line #51)
'cc_number' => $order['cc_expires'],
Replace with
'cc_number' => $cc_number_decrypt,

 

should be

 

Find (approx line #51)
'cc_number' => $order['cc_number'],
Replace with
'cc_number' => $cc_number_decrypt,

 

You are trying to find cc_number NOT cc_expires. Hopefully anyone using this noticed this typo and edited the correct line. :blush:

 

I'm packaging this up into a contrib for easy use including combining all the steps mentioned so far in this topic. I'll post it shortly.

Share this post


Link to post
Share on other sites

Thanks Nathan! Yes that helped, however, if you also have the checks/money order module, you have to add the following because when someone place an order and selects the checks/money order option, a "0" is put in the credit car field. The fix for this in the admin/include/classes/orders.php file is as follows:

 

// See if we need to decrypt - if it's already been zapped then no decrypt

$cc_number_decrypt = $order['cc_number'];

if($cc_number_dcrypt <> 0) { //This line is new

if(tep_not_null($cc_number_decrypt)) {

if(strncmp("xxxxxxxxxxxx", $cc_number_decrypt, 12)) {

$cc_number_decrypt = cc_decrypt(base64_decode($order['cc_number']), CCKEY);

}

}

} //This bracket is new

else {

$cc_number_decrypt = '';

}

 

Great contribution Nathan. This really should be worked into the next release of OsCommerce with all of the hacking and credit card theft going on.


David McGuffin - Website Developer

New Look Web Design

www.newlookwebdesign.com

---------------------------------------------------------------------

"I never memorize anything I can simply look up. . ." ~Albert Einstien

Share this post


Link to post
Share on other sites
If all you want to do is "auto-zap" the credit cards so they end up as something like "xxxxxxxxxxxx 1234" in the database, you can do this by only modifying:

 

catalog/checkout_process.php

 

Find (approx line #93)

'cc_number' => $order->info['cc_number'],

Replace with

'cc_number' => "xxxxxxxxxxxx" .  substr($order->info['cc_number'], -4, 4);

 

This is safer than encrypting the card as you're only storing enough to help the customer identify which of their cards they may have used for a purchase. This is what the 'card zapper' contrib (the most recent version) does but, like you mentioned, that's a manual process.

 

 

I just got around to trying this and it ended up not working properly. I get sent to a blank page durring the order process and the order doesn't go through. I haven't done any other changes to OSC since implementing this change and I don't have myCrypt set up to encrypt anything so it's not the same problem as edealer. Any ideas? I don't want to throw off the direction of the thread though. :o

-Tristan

Share this post


Link to post
Share on other sites
I just got around to trying this and it ended up not working properly. I get sent to a blank page durring the order process and the order doesn't go through. I haven't done any other changes to OSC since implementing this change and I don't have myCrypt set up to encrypt anything so it's not the same problem as edealer. Any ideas? I don't want to throw off the direction of the thread though. :o

-Tristan

 

Ah, I see the problem. Doh! My bad. :-" Look at the end of the code line I provided and see how it differs from what it replaced? It should end in a comma not a semicolon. This should work:

 

catalog/checkout_process.php

 

Find (approx line #93)

CODE'cc_number' => $order->info['cc_number'],

 

Replace with

'cc_number' => "xxxxxxxxxxxx" . substr($order->info['cc_number'], -4, 4),

 

When you get a blank page like that you can usually look your error.log file of your webserver and you'll see a PHP error pointing you to the problem file and line#

Share this post


Link to post
Share on other sites
Thanks Nathan! Yes that helped, however, if you also have the checks/money order module, you have to add the following because when someone place an order and selects the checks/money order option, a "0" is put in the credit car field. The fix for this in the admin/include/classes/orders.php file is as follows:

 

edealer and I have since spoken on this topic and turns out this doesn't appear to be necessary. The 0 that is generated by the checks/money order module gets encrypted and stuffed into the database just like the credit card would. When an order reads this back out of the database it's unencrypted back to its original 0.

Share this post


Link to post
Share on other sites
I don't have myCrypt set up to encrypt anything so it's not the same problem as edealer. Any ideas?
nfrobertson inspired me. HERE I created automatic mask of CC# "xxxxxxxxx1234".

For people who use gateway that already stores the CC#. See Contribution 2509 v1.3


Sam M. - Seattle

Share this post


Link to post
Share on other sites
edealer and I have since spoken on this topic and turns out this doesn't appear to be necessary. The 0 that is generated by the checks/money order module gets encrypted and stuffed into the database just like the credit card would. When an order reads this back out of the database it's unencrypted back to its original 0.

 

 

Hi Nathan.

 

I like the contribution, but I had similar problems with a cart that uses PayPal or pay-by-check.

 

The first error happened when they try to go to edit the order to add a cc number. At that point, they get an error that certain functions didn't exist in the edit_orders.php file, so I added

 

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

 

and the errors went away. However, if using PayPal or pay-by-check, they still get that same error:

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 13,

needed: 32 in

/catalog/admin/includes/functions/cc_crypt.php

on line 33

 

because there is no cc number at all in the database.

 

Any suggestions?

 

Thanks!

Share this post


Link to post
Share on other sites
Hi Nathan.

 

I like the contribution, but I had similar problems with a cart that uses PayPal or pay-by-check.

 

The first error happened when they try to go to edit the order to add a cc number. At that point, they get an error that certain functions didn't exist in the edit_orders.php file, so I added

 

require(DIR_WS_FUNCTIONS . 'cc_crypt.php');

 

and the errors went away. However, if using PayPal or pay-by-check, they still get that same error:

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 13,

needed: 32 in

/catalog/admin/includes/functions/cc_crypt.php

on line 33

 

because there is no cc number at all in the database.

 

Any suggestions?

 

Thanks!

 

 

A little more info:

 

In the edit_orders.php page, there is an input text box to enter a cc number defined like this:

 

<input name='update_info_cc_number' size='20' value='<?php echo "(Last 4) " . substr($order->info['cc_number'],-4); ?>'>

 

 

and code to update the database like this:

 

if(substr($update_info_cc_number,0,8) != "(Last 4)")

$UpdateOrders .= "cc_number = '$update_info_cc_number',";

 

 

so there is no call to encrypt the cc number here. What do I need to add to this page to encrypt the cc number before updating the database?

 

Thanks!

Share this post


Link to post
Share on other sites
A little more info:

 

In the edit_orders.php page, there is an input text box to enter a cc number defined like this:

 

<input name='update_info_cc_number' size='20' value='<?php echo "(Last 4) " . substr($order->info['cc_number'],-4); ?>'>

and code to update the database like this:

 

if(substr($update_info_cc_number,0,8) != "(Last 4)")

$UpdateOrders .= "cc_number = '$update_info_cc_number',";

so there is no call to encrypt the cc number here. What do I need to add to this page to encrypt the cc number before updating the database?

 

Thanks!

 

Cooch,

 

You appear to have a contrib loaded that I am not using yet. I'm guessing maybe Order Editor?

 

Anyway, there should be code in there that stuffs the cc_number into the database. What you'll need to do is similar to what I did in checkout_process.php. Find where the cc_number is used and encrypt it prior to the database insert/update call.

 

checkout_process.php

After (approx line #56)
$order_totals = $order_total_modules->process();
Add
$cc_number_crypt = base64_encode(cc_encrypt($order->info['cc_number'], CCKEY));

Find (approx line #93)
'cc_number' => $order->info['cc_number'], 
Replace with
'cc_number' => $cc_number_crypt,

Share this post


Link to post
Share on other sites
Cooch,

 

You appear to have a contrib loaded that I am not using yet. I'm guessing maybe Order Editor?

 

Anyway, there should be code in there that stuffs the cc_number into the database. What you'll need to do is similar to what I did in checkout_process.php. Find where the cc_number is used and encrypt it prior to the database insert/update call.

 

checkout_process.php

After (approx line #56)
$order_totals = $order_total_modules->process();
Add
$cc_number_crypt = base64_encode(cc_encrypt($order->info['cc_number'], CCKEY));

Find (approx line #93)
'cc_number' => $order->info['cc_number'], 
Replace with
'cc_number' => $cc_number_crypt,

 

 

Thanks!

 

This is what I came up with:

 

//remove this line for cc encryption

//if(substr($update_info_cc_number,0,8) != "(Last 4)")

//$UpdateOrders .= "cc_number = '$update_info_cc_number',";

 

 

$cc_number_crypt = base64_encode($update_info_cc_number, CCKEY);

 

$UpdateOrders .= "cc_number = '$cc_number_crypt',";

 

 

 

 

Does this look right?

 

cooch

Share this post


Link to post
Share on other sites
Thanks!

 

This is what I came up with:

 

//remove this line for cc encryption

//if(substr($update_info_cc_number,0,8) != "(Last 4)")

//$UpdateOrders .= "cc_number = '$update_info_cc_number',";

 

 

$cc_number_crypt = base64_encode($update_info_cc_number, CCKEY);

 

$UpdateOrders .= "cc_number = '$cc_number_crypt',";

Does this look right?

 

cooch

 

 

Using the above, I get this error now:

 

Warning: base64_encode() expects exactly 1 parameter, 2 given in

/home/audio/christianaudiohost-www/dev/catalog/admin/edit_orders.php on

line 131

 

Help!

Share this post


Link to post
Share on other sites
Using the above, I get this error now:

 

Warning: base64_encode() expects exactly 1 parameter, 2 given in

/home/audio/christianaudiohost-www/dev/catalog/admin/edit_orders.php on

line 131

 

Help!

 

 

nevermind...found my stupid error!

 

thanks!

cooch

Share this post


Link to post
Share on other sites

I'm sorry, but I'm not getting what needs to be done to edit_orders.php. I have the newest version installed, so the lines may be different than what's shown above.

 

I'm getting this error when I open an invoice to edit or to print.

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 12, needed: 32 in /home/*mysite*/public_html/*testserver*/admin/includes/functions/cc_crypt.php on line 33

 

Thanks

Annie


It's not a learning curve, it's a cliff!

Share this post


Link to post
Share on other sites
I'm sorry, but I'm not getting what needs to be done to edit_orders.php. I have the newest version installed, so the lines may be different than what's shown above.

 

I'm getting this error when I open an invoice to edit or to print.

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 12, needed: 32 in /home/*mysite*/public_html/*testserver*/admin/includes/functions/cc_crypt.php on line 33

 

Thanks

Annie

 

 

Hi Annie.

 

Not sure where you are in the whole process, but I had to delete every cc number in the orders table to get mine to work. Make a copy of the order table data first, then empty the cc_number column and see if the error goes away.

 

cooch

Share this post


Link to post
Share on other sites
I'm sorry, but I'm not getting what needs to be done to edit_orders.php. I have the newest version installed, so the lines may be different than what's shown above.

 

I'm getting this error when I open an invoice to edit or to print.

 

Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 12, needed: 32 in /home/*mysite*/public_html/*testserver*/admin/includes/functions/cc_crypt.php on line 33

 

Thanks

Annie

 

 

Annie,

 

I will preface this post with the fact that I don't have Order Editor loaded so I can't be of specific help.

 

The way this cc encrypt contrib is written (see code in step 4) it supports the cc_number being in one of three states in the database: 1) encrypted 2) blank/empty/null (i.e. nothing in it) 3) "zapped" (i.e. xxxxxxxxxxxx1234)

 

If you have real credit card numbers in there that won't work. You could delete them all like cooch suggested or you could load the card zapper contrib and zap them all.

 

An intrepid coder could write a script to convert all existing credit card numbers to encrypted form. I don't have time to do that right now but maybe in the future...

Share this post


Link to post
Share on other sites

×