How do you securely pass credit card information between pages in PHP? I am building an ecommerce application and I would like to have the users to go through the checkout like this:
Enter Information -> Review -> Finalize Order
Problem is that I am not sure on how to safely pass credit information from when the user inputs them to when I process it (at the Finalize Order step). I heard using sessions is insecure, even with encryption.
Any help would be appreciated!
I wouldn't store it anywhere. It's too much of a risk and probably not ethical.
Send a request to the payment gateway by posting a form over https and store the result of the transaction only.
You probably only care if the transaction was approved or declined. Who cares what the number is?
Don't store the credit card info in the session, don't store it to a database, don't store it to a file. Instead, write the cc info back to the review page in a hidden html inputs.
So the program flow would work like this:
User posts payment and billing information to the server via an html form.
Server verifies that this information is in the correct format (i.e., credit card has the appropriate number of digits, a billing address was entered, etc.)
After verification the server writes back all the information submitted as hidden form input fields. This includes billing address, shipping address and credit card info.
The form on the review page (with the hidden input fields) has a button labeled "Finish Order" / "Complete Order". This review form posts to the finalize order script.
The finalize script stores billing/shipping info in your database and submits the credit card info to your payment gateway.
The advantages of this method are two-fold:
You save the overhead and cost of additional PCI compliance that is required when storing credit info.
This method stays within the security bounds of the SSL protocol. Meaning, encrypted credit card info will have to be submitted to your server in any instance - this method continues to rely solely on the efficacy of SSL, without introducing the complexities of persisting credit card data.
This last point raises another concern - by having a review page you're doubling the number of times the encrypted credit card data is being transmitted across the network. With this method there are 4 transmissions minimum: client to server, server to client, client to server (again) then server to gateway. Without review there are 2 transmissions minimum: client to server and server to gateway. Is the convenience of a review page worth the risk of extra transmissions? That's a decision you as a web developer (and your client) get to make.
Well, first you should be using the HTTPS protocol to ensure that the connection is encrypted.
After that, you could store the data in the $_SESSION super-global. The data is stored on your servers, so it is relatively safe.
You could do a similar technique where you insert the information into an Order database, where the key is a GUID or something else fairly random and unique. Then, when the person goes to modify/review their order, you should have the Order ID stored in the GET part of the URL (or if you're paranoid, a cookie/session variable):
https://example.com/order?orderID=akjgflkhaslasdfkjhalsdjkljahs
To provide extra security, you could also store an IP Address in the Order Table, and make sure the IP and the Order ID match.
One alternative is to use a payment profile service like Authorize.net's Customer Information Manager (there are others too). You store the payment info in a profile via their API, then use the profile ID when actually charging the card. This way you're never storing the data on your servers.
Not my area of expertise, but I think you want to store it in a session but also use a "synchro token" (or whatever the kids are calling it these days) to help avoid CSRF attacks.
Of course, you want to be using https (correctly), avoiding sensitive data in the URL and hidden fields, avoiding putting very sensitive information in any response at all, etc., etc.
I think I will have to agree. Storing creditcard numbers is too big a risk and the consequences could be far fetched.
The ideal way would be to pass the information to a third party processor and just use the result returned to mould your script logic.
if (transaction){
// code goes here
}
else{
// code goes here
}
Hope you get the point ... :)
Related
Imagine a site like istockphoto or envato where the user can only purchase digital credits...
How would one implement this in the cleanest, easiest way using paypal's API's? The docs there are a bit confusing to navigate...
Aside from that general "best-practices" question, my guess was to start with the Integration Wizard at https://devtools-paypal.com/integrationwizard/ and select "Express Checkout - Digital goods"
After setting up some test accounts on the sandbox, I've kinda sorta got it working- but I'm confused how I track dynamic data across the session.
I.e. where would I securely store the "number of credits" and "userid" values so that it gets added to this user (who may not have an email address, nevermind one matching their paypal account) upon successful payment. Is a database/callback system necessary?
Assuming so for the time being, my guess is to maybe store the data in a database where the primary key is the TOKEN received at SetExpressCheckoutDG(), and prepopulated fields are the data I want to keep... and then the final confirm.php page will check the database at that token and implement that data... but something just seems strange about that, i.e. relying on a callback (could be timeouts, etc.) and I'm not sure if it's really the most secure way of doing it.
If using examples, please stick with PHP (not curl or other langs). Thanks!
You could just bounce this along with your calls. I'll use NVP for my examples
You set your digital goods in SetExpressCheckout. Inside, you pass how many credits the user is buying and set the PAYMENTREQUEST_0_CUSTOM with their user ID.
In GetExpressCheckoutDetails that data would be passed back to you so you could store it in some fashion (you would get their items and quantity, plus the user ID). Then, you finish with DoExpressCheckoutPayment.
If that still doesn't help let me know.
I'm building a payment gateway for my organization. Different apps will be able to POST data to the gateway to initialize a transaction. Some of that data will be 2 account codes, and the dollar amount.
Now my thoughts have moved to security. I'm concerned about end-users being able to change the POST information en-route to either end up paying less or having the funds deposited into the wrong account. To fix that I'm thinking of adding a hash that the app can pass.
I'm thinking each expected host will have a unique key. Along with their data they can send a hash of
a random salt + their key + the 2 account codes + the dollar amount, as well as sending their random salt. On the payment gateway I can then regenerate that hash with their passed information + unique key, to see if the data has been altered.
Will this be sufficient? Are there any problems with my theory?
This is basically what paymentserviceproviders use. The trick is that the "key" is private, so it is not one of the things you send in your post. You and the sender make the same hash, and as long as they are equal, there has been no tampering.
Another option that banks use is to make the payment separate from a post: generate a payment-request from the place that actually knows the prices (the server?) and communicate that number to your client. Then let them communicate with your payment gateway using this number instead of letting them posting any amount. There will be no tampering.
The first option is used by several payment providers I know, the second one is the one we use currently directly with our bank.
For added security of private information, let the result also contain hashes you know, so when someone (the user?) asks for the "thanks for paying" page, you can actually check it was the orgional user.
I am looking at integrating credit card processing into a form. Basically what happens is :
The customer will enter the website which is secured with ssl
They enter their info into a form, and select different drop down options, jquery then updates the price of their quote on the fly as they select different options.
Once the customer is happy with the price of their quote they press submit,this then posts the info to the payment page.
The customer enters their credit card number, this is then posted to the credit card processor presumably using the credit card processors script ?, along with the price to be debited from the account. (not sure on this part).
The credit card processor then returns a true or false value.
If false is returned echo "transaction failed" , else enter the customers details into the database and display a success message.
What I am wondering is if this is the correct procedure to follow ?, as the person I am doing this for was talking about saving the credit card details to the DB or sending them in an email in a csv which sent alarm bells ringing, so I told them neither are a safe option and the processing should be done by the card processing company.
I just want to clarify that the above process is correct before I suggest an alternate plan to their highly insecure one.
The easiest way to handle this is to sign up with a payment gateway. They’ll have instructions on how to communicate securely with their server. The money is then sent to an internet merchant account.
You’ll likely then need a Secure Sockets Layer certificate for your site, in order to have a secure connection with the payment gateway’s server.
An example of a payment gateway in the United Kingdom and Ireland is SagePay.
Firstly, it's not "insecure" to hold such data in a DB. If you have returning customers and you want to offer them the service of not having to re-enter information ALL THE TIME, using a DB is an idea. To make the idea secure, you'll need policies in place for the DB administration, server access and proper data security (i.e. hashing, encryption, etc)
As for your script, if you are submitting the data via POST to the PHP script, the PHP script will certainly have to talk to a DB (whether is be your local one or the connection made to the Credit Card script).
To answer your question, you'll need to know EXACTLY what the script requires before making any judgement on what you need to do locally. If the script simply takes inputs and returns a value, then you'll have to integrate it into your WebApp.
More details if any?
I'm going to use one of the payment gateways and so users from my site will be redirected to gateway hosted page to provide all the CC details. Gateway will return results to the page which I specify (lets call it paymentProcessed.php). But now my worry is that:
someone might fake it. What I mean is that someone might be redirected to payment gateway, then instead of paying, will return results to my site paymentProcessed.php page with confirmation that all has been payed. This confirmation will be send by the user itself via normal POST, and my site then will deliver products to the user although there was no actually payment done. What is the common practice to avoid this kind of situation?
Someone is redirected to gateway hosted page, pays, redirects back to my site and session he was logged in with has expired. Usually I rely on sessions to see if user should be allowed access to certain parts of the site, but now do I need to implement some other sort of check for confirmation page? For now I was thinking of storing order id and randomly generated value in database, when user redirected pass it to gateway (together with total, total would be passed to gateway and then back so I could confirm that proper amount was paid). Then when confirmation comes together with order id, my randomly generated value (and total) instead of relying on session like I usually do for normal shopping cart pages, I should check this value with matching order id and change status of order as needed. What is the common practice to deal with that kind of problem?
What other possible issues I should think about?
I tried to explain as clearly as possible and I hope all above makes sense. please let me know if I need to clarify something though. btw I code in php/mysql
It's actually easier and more secure then you realize. When using a hosted payment page, like Authorize.Net's SIM API, a hash of some sort is included that only you and the processor know about. It is impossible to fake as generating it requires private information only you and the processor have. So all you need to do is verify that the hash sent to your return page by the payment processor matches the one you have for the transaction. If it does, you can be 100% sure the transaction has not been spoofed.
Sessions tend to last longer then a trip to a remotely checkout form usually takes to complete and the session does last even though a user leaves your site. But, if you are concerned about a session expiring before they return to your site, simply store the session information in a database and use a cookie to track the user. Then when they come back use the cookie to identify them and retrieve their session information from your database.
UPDATE:
Here's how you can make your session cookie last longer with PHP:
// Makes the cookie last two hours. Make it a higher number to last longer.
session_set_cookie_params(7200);
session_start();
I've implemented some payment gateways already, one thing in common with all of them is that card processor will always return the transaction status for you, some of them use a weak redirect relaying on users to accomplish, others allows you to use their webservice to authenticate transactions. Either way you will need to get processor docs to know how to authenticate the transaction on your side.
Now about the other odds to avoid sessions from expiring you might want to store all transaction data on a table, you can also have sessions to speed up the process, but you don't need to go much further to see issues with relaying only on sessions:
What if the user gets disconnected in the middle of process?
Some CC processors force you to open a popup to process, what if the user closes it?
What if the server crashes?
What if the payment method failed and user wants to retry with another type of payment?
Now some random thoughts about payment gateway implementation:
Some processors delay to authenticate the purchase, they will return to your site that the payment has been accepted but you will have to use their webservice to check the final status;
Some processors require you to capture the purchase, meaning that even if it was approved you can void or finalize it later, this is good to avoid carders from purchasing things off your site, you can check user's info to make sure it's all good them capture or void the purchase avoiding a chargeback.
If your credit card processor gives you access to a webservice or they do server to server purchase authentication this will require a valid ssl certificate, so be aware.
That's all i can recall by now.
On step 3 I have a form which accepts a credit card, Step 4 re-prints the information including the last 4 digits of the credit card, and Step 5 I need to know the full CC # to process it and send it through my https connection to a 3rd party vendor - should I store it through hidden inputs or $_SESSION so I can access it in between the 3rd and 5th step?
FYI: My entire site is already https'd.
Take the credit card number as the last step so you don't have to store it. There are many legal issues around storing that information.
SSL won't protect data stored on disk. Additionally, PHP session data is stored by default in the file system under a temp directory with minimal permissions. So not only is the data stored in plain-text but also can be accessed by many different system users (depending on your web server configuration).
If you want to implement a multi-step checkout process I'd suggest doing some AJAX/Javascript magic on the browser side. You can collect the billing information using a series of DIVs that are hidden/collapsed and post the complete data set in one go, sending the CC data one-time to your server, which then relays the CC data to your payment processor.
Definitely not in a hidden form field. If the user walks away or saves the page or someone hits the back button, then full CC information is available. The computer may be shared with others.
If you do persist the CC to disk/database then the CC must be encrypted otherwise you would be violating Payment Card Industry (PCI) requirements. You could keep the last 4 digits in the clear separately for convenience.
Note if you go with sessions (for other reasons) you have to take care of attacks on session including but not limited to session fixation.
One other possibility is to rework your client side such that the various steps are just ajax calls (cc is in js variable not in form field) and use CSS to display/hide various divs - on the final step post the entire information to your server.
Don't store it at all. There are lots of credit card processing facilities out there. Unless you absolutely must have this functionality in house, don't do it.
Amazon Payment Services: http://aws.amazon.com/fps/
BrainTree Payment Solutions: http://www.braintreepaymentsolutions.com/
Chargify: http://chargify.com/
Paypal: https://merchant.paypal.com/
Authorize.Net resellers: http://www.authorize.net/solutions/merchantsolutions/resellerdirectory/
Seriously, take your pick.
neither way. you should store it (somehow) encrypted on the server.