PayPal: How to sanitize field values for dynamically created Encrypted Website Payments buttons?

We have successfully implemented in our Python+pyramid program Encrypted Website Payments for PayPal, except for a tiny detail: input sanitization. Namely, we would like to help the user by providing as much data as possible to the PayPal from our user database. Now, it occurred to me that a malicious user could change his name to 'Mr Hacker\nprice=0.00' or similar, and thus completely negate the security offered by EWP. I did try URL-encoding the values, but PayPal does not seem to decode the percent escapes in the file.

Our code is based on the django-paypal library; the library completely neglects this issue, outputting happily bare name=value pairs without any checks:

    plaintext = 'cert_id=%s\n' % CERT_ID
    for name, field in self.fields.iteritems():
        value = None
        if name in self.initial:
            value = self.initial[name]
        elif field.initial is not None:
            value = field.initial
        if value is not None:
            # @@@ Make this less hackish and put it in the widget.
            if name == "return_url":
                name = "return"
            plaintext += u'%s=%s\n' % (name, value)
    plaintext = plaintext.encode('utf-8')

So, how does one properly format the input for dynamically encrypted buttons? Or is there a better way to achieve similar functionality in Website Payments Standard to avoid this problem, yet as secure?

Update

What we craft is a string with contents like

item_number=BASIC
p3=1
cmd=_xclick-subscriptions
[email protected]
src=1
item_name=Percent%20encoding%20and%20UTF-8:%20%C3%B6
charset=UTF-8
t3=M
a3=10.0
sra=1
cert_id=ABCDEFGHIJKLM
currency_code=EUR

and encrypt it for EWP; the user posts the form to https://www.sandbox.paypal.com/cgi-bin/webscr. When the user clicks on the button, the PayPal page "Log in to complete your checkout" the item name displayed is "Percent%20encoding%20and%20UTF-8:%20%C3%B6". Thus, for EWP input it seems that percent encoding is not decoded.

Upvotes: 6

Views: 816

Answers (1)

Roland Smith
Roland Smith

Reputation: 43495

You could filter out key-value pairs with regular expressions;

>>> import re
>>> text = 'Mr Hacker\nprice=0.00\nsecurity=false'
>>> re.sub('[\n][^\s]+=[^\s]*', '', text)
'Mr Hacker'

Or even more simple, ditch everything after the first newline;

>>> text.splitlines()[0]
'Mr Hacker'

The latter assumes that the first line is correct, which might not be the case.

Upvotes: 1

Related Questions