nfrobertson Posted March 29, 2006 Share Posted March 29, 2006 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); } } ?> Link to comment Share on other sites More sharing options...
nfrobertson Posted March 31, 2006 Author Share Posted March 31, 2006 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'); Link to comment Share on other sites More sharing options...
MnMeBiz Posted March 31, 2006 Share Posted March 31, 2006 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 Link to comment Share on other sites More sharing options...
nfrobertson Posted March 31, 2006 Author Share Posted March 31, 2006 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. Link to comment Share on other sites More sharing options...
cinemaduro Posted April 5, 2006 Share Posted April 5, 2006 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 Link to comment Share on other sites More sharing options...
cinemaduro Posted April 5, 2006 Share Posted April 5, 2006 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) Link to comment Share on other sites More sharing options...
nfrobertson Posted April 5, 2006 Author Share Posted April 5, 2006 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. Link to comment Share on other sites More sharing options...
edealer Posted April 8, 2006 Share Posted April 8, 2006 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 Link to comment Share on other sites More sharing options...
nfrobertson Posted April 8, 2006 Author Share Posted April 8, 2006 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'); Link to comment Share on other sites More sharing options...
nfrobertson Posted April 8, 2006 Author Share Posted April 8, 2006 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. Link to comment Share on other sites More sharing options...
nfrobertson Posted April 8, 2006 Author Share Posted April 8, 2006 I have pulled this into a contribution. Encrypting Credit Card via mcrypt http://www.oscommerce.com/community/contributions,4151 Link to comment Share on other sites More sharing options...
edealer Posted April 8, 2006 Share Posted April 8, 2006 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 Link to comment Share on other sites More sharing options...
cinemaduro Posted April 12, 2006 Share Posted April 12, 2006 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 Link to comment Share on other sites More sharing options...
nfrobertson Posted April 12, 2006 Author Share Posted April 12, 2006 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# Link to comment Share on other sites More sharing options...
nfrobertson Posted April 12, 2006 Author Share Posted April 12, 2006 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. Link to comment Share on other sites More sharing options...
netstep Posted April 13, 2006 Share Posted April 13, 2006 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 Link to comment Share on other sites More sharing options...
cooch Posted April 24, 2006 Share Posted April 24, 2006 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! Link to comment Share on other sites More sharing options...
cooch Posted April 24, 2006 Share Posted April 24, 2006 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! Link to comment Share on other sites More sharing options...
nfrobertson Posted April 24, 2006 Author Share Posted April 24, 2006 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, Link to comment Share on other sites More sharing options...
cooch Posted April 24, 2006 Share Posted April 24, 2006 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 Link to comment Share on other sites More sharing options...
cooch Posted April 24, 2006 Share Posted April 24, 2006 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! Link to comment Share on other sites More sharing options...
cooch Posted April 24, 2006 Share Posted April 24, 2006 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 Link to comment Share on other sites More sharing options...
Annie Dragon Posted April 29, 2006 Share Posted April 29, 2006 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! Link to comment Share on other sites More sharing options...
cooch Posted April 29, 2006 Share Posted April 29, 2006 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 Link to comment Share on other sites More sharing options...
nfrobertson Posted April 29, 2006 Author Share Posted April 29, 2006 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... Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.