Alrighty... we're in the release candidate phase and really eager to get out, but I was encouraged by Barry Jaspan of Acquia to really bone up our default credit card security settings. My action also follows on the heels of TR's (I believe it was him) concerns about the strength of the built-in encryption routine and several other conversations about PCI compliance that never went anywhere.
While I have introduced new and potentially disruptive features, I feel it was a necessary "bug fix" to bring in prior to the RC 5. I have no further plans to modify code after this work gets committed, but I wanted to put the ideas out in the open and call for reviewers once it gets committed to Bazaar.
So, the basic problem was it took some complicated setup for stores to be PCI compliant before. This was due to the nature of Drupal's Forms API... particularly in that it was working against the ability to pass CC details between the checkout form and the review page without storing the information somehow. Further, the default settings were weak in terms of security... encryption wasn't required, for example. But that doesn't matter, because in order to be PCI compliant, you're not supposed to sensitive authentication data after CC authorization anyways. Folks not using the CVV field before obviously wouldn't have any trouble... but there's no way to keep someone from doing something inappropriate.
Basically, when the code jester of Acquia takes the time to let you know you have a problem, it's worth digging a little deeper to find a solution.
And a solution I found. It's as close as I can get in Drupal:
- On the checkout page, a customer enters their CC details.
- When the checkout form gets processed, this data will be encrypted and held in the session while we redirect to the review page.
- The review page will then immediately destroy that temporary data and put the encrypted info in a hidden field, thus bypassing the database. Furthermore, that data can only be decrypted server side with a private decryption key, so it's pretty darn safe. Combine that with SSL and maybe an armed guard posted to your web server and you're about as secure as you can possibly get.
- Post checkout, we'll store the last 4 digits of the CC number to the order's data array.
What does this mean for the future?
- Well, there is still a debug mode that you can use for testing that operates pretty similar to the previous version. I'm keeping the uc_payment_credit table in for now so we don't hose folks who have stored the last 4 digits of past CC numbers in their credit tables, but I'll remove this table as soon as we port to D6.
- Encryption will be required before you can use the credit module, even in testing. Server side encryption is necessary so we can confidently place the CC details in the form as a hidden variable.
- We can leave the encryption routine as is, because the encrypted data isn't being stored in the database as it was before. It's instead kept in the checkout form.
- No more need for a cron run to wipe CC details for abandoned orders.
- I'll be writing a tutorial on making sure you're handling CC data securely.
- Full PCI compliance by default. Sleep soundly.
Please let me know if you have any questions or concerns about the new method itself (though not about the timing of the update). I think it will work out great, and it's in testing right now on the Livetest.
This took quite a bit of tweaking in hook_form_alter() to come up with, but I'm happy with where it's going. It led to a couple code improvements in the cart and payment modules as well.




