27

I am just wondering here.. Aren't the PayPal buttons that are dynamically created, very unsecure, and easily "hackable"?

Like so:

<form name="_xclick" target="paypal" action="https://www.paypal.com" method="post">
<input type="hidden" name="cmd" value="_cart">
<input type="hidden" name="business" value="me@mybusiness.com">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="item_name" value="HTML book">
<input type="hidden" name="amount" value="24.99">
<input type="image" src="http://www.paypal.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
<input type="hidden" name="add" value="1">
</form> 

Changing the price of the product is straight forward when you can modify the code with, say, FireBug.

The reason I am asking, is because I might/will start developing an E-Commerce kinda system, where the products can be added in that system, without doing it in PayPal.

Jeff
  • 12,085
  • 12
  • 82
  • 152
  • You need to verify the price on the server. I don't know how you would do that with Paypal. – SLaks Jun 12 '11 at 13:42
  • Can you provide a reference to where you see that technique in the PayPal developers site? – Kev Jun 12 '11 at 13:44
  • Surely they would verify it on the server, in which case it's fine. – Flash Jun 12 '11 at 13:47
  • Using the answer I added below, after changing the options to what you require (i.e. Products or services, postage charges etc) it allows you to pull the costs from a database or any other source and create the buttons dynamically – lethalMango Jun 12 '11 at 14:18
  • @Kev - Sure, here you go: https://www.paypal.com/cgi-bin/webscr?cmd=p/pdn/article_pdn_storefront-outside – Jeff Jun 12 '11 at 15:02
  • @SLaks - Thats what I thought, too - with the IPN – Jeff Jun 12 '11 at 15:02
  • @Tim - That is exactly why I am asking this. ;) – Jeff Jun 12 '11 at 15:03

6 Answers6

48

You should use the PayPal Button API such as below:

$sendPayData = array(
    "METHOD" => "BMCreateButton",
    "VERSION" => "65.2",
    "USER" => "username",
    "PWD" => "password",
    "SIGNATURE" => "abcdefg",
    "BUTTONCODE" => "ENCRYPTED",
    "BUTTONTYPE" => "BUYNOW",
    "BUTTONSUBTYPE" => "SERVICES",
    "BUTTONCOUNTRY" => "GB",
    "BUTTONIMAGE" => "reg",
    "BUYNOWTEXT" => "BUYNOW",
    "L_BUTTONVAR1" => "item_number=$invoiceNumber",
    "L_BUTTONVAR2" => "item_name=$invoiceType",
    "L_BUTTONVAR3" => "amount=$invoiceTotal",
    "L_BUTTONVAR4" => "currency_code=GBP",
    "L_BUTTONVAR5" => "no_shipping=1",
    "L_BUTTONVAR6" => "no_note=1",
    "L_BUTTONVAR7" => "notify_url=http://www.abc.co.uk/paypal/ipn.php",
    "L_BUTTONVAR8" => "cancel_return=http://www.abc.co.uk/paypal/thanks",
    "L_BUTTONVAR9" => "return=http://www.abc.co.uk/paypal/return.php"
);

You can then send that with cURL to their API

$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_URL, 'https://api-3t.paypal.com/nvp?'.http_build_query($sendPayData));
$nvpPayReturn = curl_exec($curl);
curl_close($curl);

To then generate a encrypted HTML button that cannot be edited

<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> 
<input type="hidden" name="cmd" value="_s-xclick"> 
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----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-----END PKCS7-----
">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_paynow_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online."> 
<img alt="" border="0" src="https://www.paypal.com/en_GB/i/scr/pixel.gif" width="1" height="1"> 

These links should help you with the button options:

https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_BMCreateButton

https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ButtonMgrAPIIntro

lethalMango
  • 4,433
  • 13
  • 54
  • 92
  • Awesome! Will this require any API keys though? – Jeff Jun 12 '11 at 15:04
  • The SSL is handled by paypal by passing the data to a secured location (`https://api-3t.paypal.com`). All you need to do in order to setup is login to your paypal account, go to profile, request an API signature (not the other option - I cant remember what its called) and then you are away. – lethalMango Jun 12 '11 at 16:47
  • @lethalMango - Isnt it possible to make it secure, without sacrificing simplicity for the end-user? I believe I have seen scripts that does it all for you, no need to config anything in PayPal. – Jeff Jun 12 '11 at 17:10
  • 2
    It would be fairly easy to simplify the lot for the end user. All of the options are variables i.e `"BUTTONTYPE" => "BUYNOW"` can be `BUYNOW` `CART` `SUBSCRIBE` etc. If you supplied the user with a form with predefined options taken from the manual, then you would effectively be creating a user friendly PayPal payment system with no coding required on the users part. You will always need to setup your own paypal account to be able to use all scripts (as far as I am aware) as that would reduce security overall otherwise. A simple few clicks on PayPal for an API key shouldn't be an issue – lethalMango Jun 12 '11 at 20:46
  • @lethalMango - as far as I know, the scripts I have seen requires no API stuff. :) – Jeff Jun 12 '11 at 22:33
  • @Jeff, I'd be interested to find out how they do it for my own projects. Do you have any examples to hand? – lethalMango Jun 12 '11 at 22:49
  • @lethalMango - Here you go, I forgot its Open Source now, I own the retail: http://butterflyopensourcecode.com/ - maybe we should check out their code huh? – Jeff Jun 13 '11 at 10:50
  • @lethalMango - Coolbeans - hey, could you add me on Skype? :) This is interesting. This is my Skype ID: jeffijoe – Jeff Jun 13 '11 at 13:47
  • @lethalMango I'm trying to use BMCreateButton in .Net, exactly the way you do in PHP. I setup a sandbox business account, I got API credentials, I wrote the code, it works, I get the BMCreateButton answer back, but when I put the HTML(generated by BMCreateButton) on a web page: When I click the "Buy now" button I'm taken to the paypal website, which actually SHOW all the value (item name, item price...) BUT it say "There was a problem with the decryption of your secure order. Please contact your merchant." Any idea about where is the error? I didn't setup any SSL certificate, does this matter? – Max Apr 02 '12 at 15:20
  • @Max are you trying to create a donation, subscription or cart button? There are various different causes for that error message which relate directly to the type of button you are attempting to create. You don't need to setup any SSL on your end using this method. – lethalMango Apr 02 '12 at 22:27
  • @lethalMango Here is the code I'm using http://stackoverflow.com/questions/9939960/how-can-i-call-functions-of-buttonmanager-nvp-paypal-api I'm trying to create a PayNow button, I dont use cart, I only need the customer to pay for a single product/service – Max Apr 03 '12 at 07:36
  • @lethalMango Thank you for the answer. But suppose I need to do this in a Java web app. So, I cant call cURL via PHP. How am I gonna generate the dynamic button in that case please? If could switch to some alternative other than cURL it's fine, please list if any; but I see that PayPal is recommending to use cURL in their help pages for using the buttonManager API. – nadh Jan 28 '14 at 09:12
  • @nadh cURL is just a HTTP client, Java has one, too. – Jeff May 18 '14 at 11:32
  • What are the username and password fields? – Daniels Šatcs Mar 19 '16 at 21:42
  • 5
    This is so much more useful than the Paypal Documentation – Martin May 13 '16 at 07:31
  • Sorry how can I manage the response stored in $nvpPayReturn? Many thanks – xdola Jun 26 '17 at 13:32
  • @lethalMango Is this code still safe to use (security issues, updates, etc)? I'm asking, because the original answer is from 2011, and the official documentation isn't as useful. – FiddlingAway Aug 10 '17 at 08:54
  • 1
    @FiddlingAway Still safe, there may have been API updates since but the method is still sound. – lethalMango Aug 10 '17 at 18:48
8

You're right - dynamic PayPal buttons are easily "hackable" if you pass, for example, the price of the product in clear text.

However, PayPal supports public-key button encryption, so that the relevant details can not be easily altered. This is the way it works:

  • You generate a public/private key pair with an appropriate program such as OpenSSL.
  • You log in to your PayPal account and submit the public key to PayPal, then store the private key securely on your Web server. You will also need to download PayPal's certificate and store it on your server as well. It is also highly recommended to tell PayPal not to accept unsigned/unencrypted transactions (see link at bottom for details).
  • Each time you need to generate a PayPal button, you encrypt the data using PayPal's public key and sign it with your private key, then you display the result on your Web page. When the user clicks the button, PayPal will decrypt the details and check they have not been tampered with since their generation on your server.

This way, as long as your private key is uncompromised, no one will be able to alter the transaction's details.

More information and detailed instructions are available at https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_encryptedwebpayments#id08A3I0P017Q. (Although PayPal provides its software to generate encrypted buttons, I think it's possible to create them "on the fly" using appropriate functions, such as openssl_*() in PHP; I haven't tested them personally).

An alternative would be implementing Instant Payment Notification (https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro); you could check that the amount of the transaction performed by the user equals the total order amount.

Alessandro Menti
  • 1,290
  • 20
  • 28
  • I have created a plugin for Jekyll - a popular static site generator - that uses this technique (which paypal refers to in their docs as "Encrypted Web Payments"). The plugin is [here](https://github.com/MoralCode/Jekyll-EWP) if anyone is interested (yes i realize this question was about PHP originally, just looking to hopefully help someone looking for dynamically generated and secure paypal buttons on a static site) – MoralCode Jan 15 '21 at 05:59
3

I think you can use a hashed approach as well where all the important values are hashed so they can't be modified.

The current approach is indeed hack-able but once you're on the PayPal site you can still see the amount you're going to pay. It's up to the user to double check the amount really.

Halcyon
  • 57,230
  • 10
  • 89
  • 128
1

You can créate encrypted buttons on the fly, for more info you can check Dynamic Paypal button encryption

Community
  • 1
  • 1
Rafael
  • 2,827
  • 1
  • 16
  • 17
1

You are correct. <input type="hidden" name="amount" value="24.99"> can be easily manipulated on the client side. In the example you gave, this might be a form where the client is actually supposed to be able to set the amount, eg. a PayPal donate button. Otherwise there would need to be server side checks after this form submission to ensure that there is no funny business going on.

Asaph
  • 159,146
  • 25
  • 197
  • 199
-9

I think I have a solution for this issue:
First, submit to PayPal from a secure page - Public SSL.

Second, you can use Ajax in order to prevent users to brows your HTML code via "Right Click - View Source" or browsers Tools like Fire-Bug.

Here is an example in jQuery:
I usually program with C#.NET so this is why I communicate with a .ashx Generic Handler (but it can work with PHP as well)

$(function () {

$.ajax({  
           type: "POST",  
           url: "myPage.ashx",  
           data: {  
               theProductsIdAndAmountsString: yourValue  
           },
           success: function (allHtmlCode) {
               $("body").append(allHtmlCode);
               $("form").submit();
           }
       });
   });

In the server side you can generate all the HTML form by pooling the data from your Data Base, Then send it back to the page.
After, append it to the body and submit the form to PayPal.

Now no one can use browser tools like Fire-Bug to change your HTML values.

  • I did think of doing the form submission thing, however not with Ajax, so +1. I dont suppose it is possible to edit the Javascript with tools like FireBug, huh? :) – Jeff Jun 13 '11 at 11:04
  • Javascript you can edit with FireBug, but the variables you send to PayPal will be generate in the server, that you cant edit, And you cant see the source code ass well, I tried .. It is not possible.
    But there are are tools that allow you to decode the request to the server, edit the variables values and re-request to the server with the new values. This is why you should use SSL (https://) to encrypt the request to the server and prevent that too.
    – Shavit Cohen Jun 13 '11 at 22:06
  • @Shavit - is it possible to use SSL without paying a fortune? (Do I need to purchase any certificates, or how does it work?) – Jeff Jun 14 '11 at 10:09
  • @jeff - Sure, you don't suppose to pay a fortune anyway, all you need is a dedicated IP, You should ask your web host about it, it should cost something like $2 a month. – Shavit Cohen Jun 14 '11 at 15:48
  • I suppose this is the best approach, to avoid having the customer deal with API stuff. :) – Jeff Jun 15 '11 at 13:57
  • 6
    This method does not provide any security. The ssl makes no difference, you can still get the browser to submit whatever you want to the server. For example, try disabling javascript in your browser. You can then open firebug easily, and submit the form. This is like putting your key under the doormat. – DaedalusFall Oct 10 '11 at 11:15
  • 1
    Also you should check out the Firefox extension named Tamper Data, which whenever you fire a request to a server, stops your browser, lets you edit the values, and then continue submitting. Of course SSL prevents APR attacks, and such, but still the user has full control over what is sends. – Tamás Barta Feb 28 '12 at 13:43
  • You should never rely on the client to validate data – dreadiscool Jun 15 '14 at 12:43
  • This is not a solution to the problem – user2520969 May 27 '17 at 23:35