Reputation: 83
I have implemented Stripe into a Laravel project. I have a question regarding security and best practice with Stripe on my server's side.
As an example, I have three subscription plans available for my site (let's say free trial, basic and professional). After Stripe verifies the user's payment and sends me the transaction token , I send this token together with the user's choice of subscription to my server where I then actually charge the user. This snippet shows the form submission code which performs this process.
$('#ButtonForBasicSubscription').click(function(){
var token = function(res){
var $token = $('<input type=hidden name=stripeToken />').val(res.id);
var $plan = $('<input type=hidden name=selectedPlan />').val('basic');
$('form').append($token,$plan).submit();
};
StripeCheckout.open({
key: 'pk_my_publishable_key',
address: false,
amount: 100,
currency: 'gbp',
name: 'My Product',
description: 'Basic Subscription',
panelLabel: 'Make Payment',
token: token
});
At my server, I then test the selectedPlan input and then according to that value, charge the user using the stripeToken.
My concern is with the way that I am sending the selectedPlan variable. I can't think of a better way to do this (although I am sure there must be), and my concern is that it would be possible for an (evil) user to intercept the Stripe token and submit it to my server with a different selectedPlan value (for example someone selects the basic subscription, completes the payment to Stripe, but then submits the returned token with a "professional" selectedPlan value instead of a "basic" selectedPlan to my server). This would give the user a Pro subscription having paid for a Basic subscription.
Is this even possible? or have I crossed the line from being a neutral sceptic to being wildly over-paranoid.
Any help or guidance is greatly appreciated.
Upvotes: 1
Views: 138
Reputation: 2998
It sounds like you already know this and just need assurance, so I can provide that.
With Stripe, a token - indeed - merely means that the user's credit card has been received by their server and is awaiting / allowing a charge from you.
Then, you use that token to make a charge, in this case, based on the selectedPlan
variable. If the user maliciously changes that variable between sending their credit card and you receiving the token, you're still going to charge based on whatever that variable is, so you're still golden.
Stripe does not care what the dollar amount value they receive with the initial card through their javascript encryption is - in fact, they may not even send / store that information. What they care about is the dollar amount that you send through the PHP API, and that is the amount that is actually charged to the card.
So, if your user submits their card information after clicking on a basic
plan dollar amount, then somehow your server receives a professional
variable due to their hackery, you can rest assured that their card will still be charged based on that professional
cost because your PHP script will Stripe_Charge based on that professional
variable if your code is set up correctly.
You don't need HTTPS to do this correctly/safely - you honestly don't even need a CSRF filter, because as long as you're charging for a specific plan and then associating that specific plan with the Auth::user() after the successful transaction (be sure to wrap your Stripe_Charge in the appropriate Stripe_CardError try/catch), there's no way to trick you.
The only place a well-written app genuinely needs a CSRF filter is on login forms, so long as all other inputs to our server our checked against Auth::user() for permission. But of course, it's still a good idea to CSRF protect every post'able route on your app just to discourage bandwidth-hogging abuses.
Upvotes: 3