bradcathey has asked for the wisdom of the Perl Monks concerning the following question:
Fellow Monasterians,
Okay, before you implore me to Super Search, I'm aware of the many nodes on PM dealing with credit card security, encryption, SSL, etc. But a short comment by Zaxo here, that I just stumbled across, got me to wondering about something I am currently doing on an e-commerce site. Here's the scenario:
- after reviewing the shopping cart (screen 1) and entering their contact info (screen 2), the customer enters the credit card info on a secure form (screen 3)
- my Perl encrypts the CC# with Crypt::CBC and places it in a "temporary" record in a MySQL table id'ed by session cookie ID
- customer then sees a summary screen (screen 4) with only CC# snip (xxxx-4321) showing
- customer clicks the purchase button and the CC# is retrieved, decrypted, and processed by Perl using gateway conventions
- if successful, the CC record is deleted from the database and the cookie is force-expired
For the sake of argument, let's say temporary storage is safe. But what if the customer bugs out at the summary screen and never makes the purchase, thus never deleting the record? Yikes, I'm storing their CC# when told them I wouldn't.
If I don't store it in a database, where do I store it for the short time I need it? I could:
- skip the summary screen and just process it (but that goes against current online convention)
- encrypt them and store them as cookies on the user's machine (merlyn once suggested this in the CB for passwords). I could set the expiration for 5 minutes and not have to worry about deleting it from the DB
Any other ideas for how to temporary store that CC# from the time they submit it to the time they click the Purchase button? Thanks!
Update: In case it matters, I have a shared hosting account on Pair, and use their SSL certificate.
Update 2: So, after a day of watching the replies to my OP, I'm thinking I should encrypt the CC# and place it in a hidden field on my summary screen and either stick with the single key I have already *or* randomly generate a key for that session and store it in a cookie. Am I getting close?
—Brad "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
Re: Storing credit card numbers temporarily (OT)
by fokat (Deacon) on Aug 14, 2005 at 04:30 UTC
|
bradcathey made a very wise question... And deep inside he knows the answer to the question he has asked
Yikes, I'm storing their CC# when told them I wouldn't.
Then by all means, don't. You already have a very nice setup going on... You're using strong crypto to protect the credit card. What you need to store in your database, is a random secret indexed with the key you already have.
Then you use this random secret as the key to encrypt the CC number, as you already described, and return this encripted credit card number back to the browser.
By doing this, you won't be storing the CC number (your customer will), and you will be protecting the CC number within the cookie jar of your customer's browser.
The returned cookie can be set to expire in a reasonable time, just as the secret in the database.
Best regards
-lem, but some call me fokat
| [reply] |
Re: Storing credit card numbers temporarily (OT)
by phroggy (Monk) on Aug 14, 2005 at 03:23 UTC
|
The way I see it, you've got three basic options:
- Store the credit card number server-side
- Store the credit card number client-side
- Use some combination of both
If you store it server-side, you can add it to an SQL table with an expiration time, and periodically DELETE FROM Table WHERE expiration<NOW(). Since it's important for security reasons that the information actually be deleted, I'd suggest doing this from an hourly (or more frequent if you'd like) cron job. Note that if the cron job runs immediately after the user clicks Preview, that's OK, because the expiration time will be some point in the future.
If you store it client side, you can either use a cookie (this strikes me as being a VERY BAD IDEA, because cookies are stored plain-text and will not be deleted immediately if the browser crashes or something), or you can a hidden form field, e.g. <input type="hidden" name="cc" value="nnnnnnnnnnnnnnnn">. Yes, this does mean that the value will be sent back to the client and they can see it if they view source, but as long as you're using SSL, I don't really see a major problem with this. Maybe add a Pragma: No-cache header or something, just to be sure.
In either case, you can obfuscate the number somehow, to make it non-obvious if anyone should stumble across it somehow.
Your third option would be to encrypt the number, store the encrypted form on the server as described in #1, and send the (randomly generated) decryption key to the client (via a hidden form field as described in #2). If someone hacks the server, the encrypted numbers are useless without the key, and if someone hacks the client, the key is useless by itself. I've never done something like this, but I'm sure someone else here has suggestions for implementations.
Although the cron job I suggested in #1 is still a good idea for #3, it wouldn't be absolutely required. I'd just add the delete query at the beginning of each script that accesses the database, so as long as the site is being actively used, old data is getting deleted periodically.
Do you actually have an official policy that says you won't store credit card numbers? Check the wording on that. How hard would it be to revise?
Personally, I'd recommend option #2.
perl -e '($,,@_)=("er",",\n","l Hack"," P","Just anoth"); print reverse @_;'
| [reply] [d/l] [select] |
|
Points well taken, phroggy.
Clarification: our privacy policy states that we will not "permanently" store their credit card number but that it will only be kept for the duration of the transaction.
—Brad "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
| [reply] |
Re: Storing credit card numbers temporarily (OT)
by gam3 (Curate) on Aug 13, 2005 at 23:50 UTC
|
Could you expand on how you are storing the keys?
Is there only one key for all of the the users?
If you create a new key each time a user submits a CC# then you can store the key in the final submit form in a hidden field. Now you can only decypt the CC# that is in the database when the final form is submitted.
As for the database entry I would have an order AND a cancel button on the web page, so that the user can delete the CC# from the database if they want. You should also expire (delete) the CC# in the database after some (relativly short) amount of time.
-- gam3
A picture is worth a thousand words, but takes 200K.
| [reply] |
|
If a user wants to cancel, there's absolutely no reason to think that they'll actually bother to click your cancel button, instead of just clicking some other link. Go ahead and put a cancel button, but it shouldn't actually do anything besides redirect them to another page.
perl -e '($,,@_)=("er",",\n","l Hack"," P","Just anoth"); print reverse @_;'
| [reply] [d/l] |
|
Hmmmmmm, I only have one key for all. Currently it is stored in a chmod'ed 600 folder off my home directory (not root). I was told this was the safest place. I like your idea of a different key for each! But why in a hidden field? Isn't that too obvious? What about a cookie?
I did think about the cancel button, but there is still a chance they will bail without clicking it.
And how do you delete a record from a database automatically? Cron job? Thanks!
—Brad "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
| [reply] |
|
There is no security difference between a cookie and a hidden field in a form on the client side. They are both likeley to be stored on the hard disk. Having the key in the form just binds it closer to its use so it is less likely to leak out. If you were careful you could get the same effect with cookies -- using path etc.
It does not matter if someone has the key on the client machine, because the CC# is on your computer. And if they get into the database they will need the key from each client to get the stored CC#s.
Yes you can run a cron job to remove the old entries.
-- gam3
A picture is worth a thousand words, but takes 200K.
| [reply] |
Re: Storing credit card numbers temporarily (OT)
by donarb (Beadle) on Aug 14, 2005 at 16:43 UTC
|
Another option not mentioned here is to not handle the credit card number at all.
Some CC processors will allow you to insert a custom page into your ordering flow. The customer jumps to the page hosted at the processor that accepts the cc number and other info. On approval (or denial) the customer is redirected back to your site to continue the ordering process.
I did this on a site I wrote and I was happy that I didn't have to worry about liability issues with the client's server storing credit card numbers.
If you really want to store sensitive information on your server, take a look at the book "Translucent Databases" by Peter Wayner. This book shows examples of how to store data securely. | [reply] |
Re: Storing credit card numbers temporarily (OT)
by mattr (Curate) on Aug 14, 2005 at 15:08 UTC
|
This may be seen as a bit much but in a post way back when I noted that you can find secrets by dumping the memory device to a file and grepping it. I was able to eliminate this danger by writing junk characters to the secret string to fill its entire previous length before destroying it.
When I have been in similar situations to yours I would overwrite the string after using it, and if it had to remain across states, then would encrypt it and store it as a hidden form field in the html returned to the user. Otherwise you can store a hash or just the last 4 digits if you wish. I believe the crypt function is one-way too; if you are using mysql you can store it in a password field. But you should do periodic cleaning, I would always clean old sessions after finishing a user's transaction. | [reply] |
Re: Storing credit card numbers temporarily (OT)
by TedPride (Priest) on Aug 14, 2005 at 22:14 UTC
|
Depends. If you do your credit card processing offline (like my parents' mail order business), the numbers will have to be stored temporarily anyway. Storing them encrypted with an expiration timestamp should be sufficient, since anyone with access to your hosting account will also be able to edit your ordering system to redirect the credit card numbers elsewhere.
If on the other hand your credit card processing is done in real time, just pass the number encrypted in a hidden form field. Part of the key will stay the same and not be passed, and part will be randomly generated and passed in a hidden form field, perhaps encrypted as well. This prevents someone on the user end from easily decrypting the credit card number, and also prevents someone on the outside from somehow cracking one number and then using the same key to crack every other number. | [reply] |
Re: Storing credit card numbers temporarily (OT)
by shiza (Hermit) on Aug 15, 2005 at 17:40 UTC
|
You could also re-order the check out process so the user enters their billing information after or on the summary page.
This would elminate the need to store their credit card number.
What about notifying the user that their credit card information will not be removed until the order is completed and give them an option (similar to cancel) to manually remove their information.
You could also keep track of user activity and have a process delete records from your temp table if the session has timed out. | [reply] |
Re: Storing credit card numbers temporarily (OT)
by inman (Curate) on Aug 17, 2005 at 15:29 UTC
|
The thing to do is to look at the ways in which a hacker might attack the system in order to get the credit card details and assess the risk.
The customer uses an SSL enabled browser to access your application. We have to assume that this is secure as the communications are going to get. If SSL is compromised then everything is in the open. The browser may similarly be compromised. If this is the case then all of the customer's transactions are compromised.
The application stores the encrypted credit card number in a database such that it is written to disk. The encryption needs a key which also has to be stored. The encrypted records are written to disk which means that they could be recovered even if they have been deleted from the database. Storing the credit card information to non-volatile storage is a security headache.
The application is hosted on a third party computer that may also host other peoples code. You cannot assume that the system is secure at all. It is subject to both physical and remote access from other people. This issue is best tackled with expensive SLAs, contracts and indemnity clauses.
If I was doing this I would avoid the problem all together and leave out screen four. The customer supplies their credit card details once and the application sends it for processing. If you do this then you don't need to store their credit card info to a database at all. | [reply] |
|
|