256
Logo

Paypal Website Payment Encryption for Perl

This page documents my work in using encryption to increase the security of Paypal transactions (buy buttons, shopping carts, etc.) using Perl or shell scripts.

Background

Paypal uses a series of HTML fields to transfer data about what the user is paying for. You might have something like the following on your webpage:

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick" />
<input type="hidden" name="business" value="bad@foo.com" />
<input type="hidden" name="return" value="http://foo.com/thanks.html" />
<input type="hidden" name="item_name" value="Donation" />
<input type="hidden" name="amount" value="10.00" />
<input type="submit" value="Donate $10" />
</form>

The problem is that the HTML is out there for all to see. Spiders can see and visit where paypal will return you when the transaction is done (return field). Phishers and spammers can (and do) harvest the email address which receives the paypal mail. Hackers can change the value or quantities meaning that your system has to verify the amounts before delivering the product. There are best-practices and hacks that work around some of these problems but they don't go far enough.

Solution -- Encrypt the Fields

Paypal has a solution called Encrypted Website Payments (paypal login required). Here's the EWP manual which doesn't seem to require a login. With EWP, the fields and values are signed with a local certificate which is uploaded to the Paypal servers and encrypted with a public certificate which Paypal distributes. All of this stuff is wrapped up using S/MIME and is used instead of the hidden HTML fields.

<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-----
MIIG5QYJKoZIhvcNAQcDoIIG1jCCBtICAQAxggE6MIIBNgIBADCBnjCBmDELMAkG
A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBKb3Nl
...
wkTGxwAxwWownnk9yzWnyPpK7InDhQIGFrobpf/kpfw9tkORgYR+Ufa9gcOa3Xg/
KpWp9N88uBHP/W225LYHH3AMgHi0HqQJum+8JdfWvvt5NSdJJMfTz9Y=
-----END PKCS7-----
" />
<input type="hidden" name="amount" value="10.00" />
<input type="submit" value="Donate $10" />
</form>

Now you can encrypt your button arguments if you use Paypal's button creation pages but that does not allow you to make changes to the arguments dynamically. What I wanted was the ability to encrypt the buttons via cgi scripts on my site. Paypal supports this but on the default pages, their only sample code is for Windows C and Java. I finally found out about the API sourcebook pages which have details about using PHP and shell encryption scripts. Thanks much to Dave and the rest of the authors of these documents for the help.

My Improvements

I added a Perl version of the encryption script which calls openssl directly and not the shell script. I've modified their shell script a bit to make some improvements. I've added some documentation, improved the variables, and removed the need for a temporary file. The variables for the transaction are in the params.txt file but could be coded into the shell or perl script if you were doing the buttons on the fly.

How To Get It To Work

To get the encryption to work for your system you need to following the following steps. Feedback welcome.

  1. To do any testing with the Paypal Sandbox which doesn't impact live accounts, you need to sign up for the Paypal Developers Network and log into a Sandbox account.
  2. Download the above scripts, sample certificate, sample key, sample paypal certiciate, and sample html files.
  3. Make sure that the Donate button in the sample HTML file works for you. You have to sign up for the Paypal Developers Network for this to work. They may have changed the test account or something so failure here is not necessarily a killer.
  4. Initially run the Perl or shell script to generate a new html file using the sample encryption certificate and key. Every time you run the script, it will generate different output even with the same input because of random number generators. Watch for openssl errors. I had to upgrade to 0.9.7e to stop my 0.9.7d version from segfaulting.
  5. Make sure the Donate button in the new html file works. If it gives the same error message as the sample file, then they may have changed the test account. This may not be your problem.
  6. Generate your own private key file using OpenSSL.
    openssl genrsa -out my_key.pem 1024
    
  7. Generate a OpenSSL configuration file for your encryption certificate information. Edit it to adjust your Country (C), State (ST), City/Local (L), organization (O), and organization unit (OU). I use my domain name for O and OU.
    cat > openssl.cnf <<EOF
    [ req ]
    prompt = no
    distinguished_name = req_distinguished_name
    [ req_distinguished_name ]
    C  = US
    ST = Your state
    L  = Your city 
    O  = YourDomain.com
    OU = YourDomain.com
    EOF
    
  8. Generate your own public certificate file using OpenSSL. The '-days 365' option specifies that the key will expire in 365 days.
    openssl req -new -config openssl.cnf -key my_key.pem -x509 -days 365 -out my_cert.pem
    
  9. Upload the my_cert.pem public certificate file you just created to your My Account > Profile > My selling tools > More selling tools (at bottom) > Website Payment Certificates page. Click the Add button at the bottom of the page. If you want to start working with their Sandbox then you should upload it to your Sandbox Website Payment Certificates page.
  10. Download Paypal's public certificate from the Website Payment Certificates page. Save it as paypal_cert.pem. If you are going to be testing with the PDN Sandbox then save the key from Sandbox Website Payment Certificates page to paypal_sandbox_cert.pem.
  11. After you've uploaded your certificate, go back to the Website Payment Certificates page and copy the Cert ID associated with the certificate.
  12. You may need to edit the perl or shell script and adjust the following variables:
    • MY_KEY_FILE to my_key.pem. Can be a full path.
    • MY_CERT_FILE to my_cert.pem. Can be a full path.
    • PAYPAL_CERT_FILE to either paypal_cert.pem or paypal_sandbox_cert.pem. Can be full paths.
    • OPENSSL variable to the full path to the openssl utility.
  13. Edit the params.txt file to change the paypal fields for your transaction. This file could be coded into the shell or perl script if you were doing the buttons on the fly.
    • business -- to your paypal email address
    • cert_id -- to the Cert ID from the Website Payment Certificates page
    • item_name -- the name of the item that is shown to the buyer
    • amount -- amount of your item
    • ...
  14. Run the script and redirect the output to a test HTML page. Watch for any openssl errors.
  15. Make sure the Donate button in the new html file works. You should not get errors now. You can get decryption errors, missing parameter errors, or others. Go back and make sure you followed the instructions carefully.
  16. If it works then you are ready to integrate your encrypt script into your site.

Enjoy. Feedback welcome.

Free Spam Protection   ORMLite Java ORM   Android ORM   Simple Java Magic   JMX using HTTP