Hack 37 Hack-Proof Your Buttons with Encryption

figs/expert.gif figs/hack37.gif

Add yet another layer of security to a Buy Now Button by encrypting its contents with OpenSSL and C/C++.

Now that you've created a complete Buy Now button [Hack #28], how can you prevent potential hackers from seeing (and possibly changing) the information you're passing to PayPal? PayPal's button encryption enables you to hide the exact contents of your HTML form in a PKCS7-encrypted blob.

While it is not necessary to integrate button encryption into every web site, it does allow you to provide another layer of security without affecting your customers' buying experience.

This hack shows how to secure the contents of a button using OpenSSL and C/C++. For a simpler solution, see [Hack #36] .


4.11.1 OpenSSL and Keys

Button encryption is done using a cryptography library, such as OpenSSL, and a pair of cryptographic keys. OpenSSL is nice, because it allows you to both sign and envelope the message in one action. The first thing to do is install OpenSSL, which is available for download at http://www.openssl.org.

Note that some knowledge of compiling programs is required for the installation of OpenSSL on Unix. Instructions for compiling and installation on various platforms can be found in the OpenSSL download. A precompiled Windows version is available at http://www.slproweb.com/products/Win32OpenSSL.html. Simply follow the installation instructions for your particular environment.

Cryptographic keys must be exchanged in order for button encryption to work. You'll need to contact PayPal to obtain PayPal's public key, and you must provide your public key to PayPal. You should generate your keys in PEM format; consult the OpenSSL documentation (http://www.openssl.org/docs/HOWTO/keys.txt) for details.

4.11.2 Basic Button Encryption Using OpenSSL

Start with an unencrypted HTML form tag in your HTML page:

<form method="post" action="https://www. paypal.com/cgi-bin/webscr">

<input type="hidden" name="cmd" value="_xclick">

<input type="hidden" name="business" value="sales@company.com">

<input type="hidden" name="amount" value="1.00">

<input type="hidden" name="currency_code" value="USD">

<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but23.gif"

                name="submit" alt="Make payments with PayPal - it's fast, free 

                and secure!">

</form>

The first thing you need to do is convert all the hidden field name/value pairs from this form into a single string, like this:

cmd=_xclick

business=sales@company.com

amount=1.00

currency_code=USD

Keep in mind that the line feeds required are Unix line feeds (\n), not Windows line feeds (\r\n). Ensure that your program is creating the string correctly or you will get decryption errors when posting your encrypted form.


Next, load the PayPal public key from the paypal_cert.pem file:

BIO *bio;

X509 *gPPx509;

char* payPalCertPath = "/opt/keys/paypal_cert.pem";

if ((bio = BIO_new_file(payPalCertPath, "rt")) == NULL) {

        printf("Fatal Error: Failed to open (%s)\n", payPalCertPath);

        goto end;

}



if ((gPPx509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL) {

        printf("Fatal Error: Failed to read Paypal certificate from 

                (%s)\n", payPalCertPath);

        return "";

} 



BIO_free(bio);

Then, load your public and private keys:

X509 *x509 = NULL;

RSA *rsa = NULL;



char* certPath = "/opt/keys/my_cert.pem";

char* keyPath = "/opt/keys/my_key.pem";



if ((bio = BIO_new_file(certPath, "rt")) == NULL) {

        printf("Fatal Error: Failed to open (%s)\n", certPath);

        goto end;

}



if ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL) {

        printf("Fatal Error: Failed to read certificate from (%s)\n", certPath);

        goto end;

}



BIO_free(bio); 



if ((bio = BIO_new_file(keyPath, "rt")) == NULL) {

        printf("Fatal Error: Failed to open (%s)\n", keyPath);

        goto end;

}



if ((rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL)) == NULL) {

        printf("Fatal Error: Unable to read RSA key (%s).\n", keyPath);

        goto end;

}



BIO_free(bio);



' Create an EVP_PKEY instance from the private key you just loaded:

EVP_PKEY *pkey = EVP_PKEY_new( );



if (EVP_PKEY_set1_RSA(pkey, rsa) == 0) {

        printf("Fatal Error: Unable to create EVP_KEY from RSA key\n");

        goto end;

}



' create the PKCS7 instance so you can create the PKCS7 Blob:

PKCS7 *p7 = PKCS7_new( );                

PKCS7_set_type(p7, NID_pkcs7_signedAndEnveloped);



PKCS7_SIGNER_INFO* si = PKCS7_add_signature(p7, x509, pkey, EVP_sha1( ));



if (si) {

        if (PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT,

                                OBJ_nid2obj(NID_pkcs7_data)) <= 0) {

                printf("OpenSSL Error: %s\n", ERR_error_string(ERR_get_error( ), NULL));

                goto end;

        }

} else {

        printf("Fatal Error: Failed to sign PKCS7\n");

        goto end;

}



//Encryption

if (PKCS7_set_cipher(p7, EVP_des_ede3_cbc( )) <= 0) {

        printf("OpenSSL Error: %s\n", ERR_error_string(ERR_get_error( ), NULL));

        goto end;

}



if (PKCS7_add_recipient(p7, gPPx509) <= 0) {

        printf("OpenSSL Error: %s\n", ERR_error_string(ERR_get_error( ), NULL));

        goto end;

}



if (PKCS7_add_certificate(p7, x509) <= 0) {

        printf("OpenSSL Error: %s\n", ERR_error_string(ERR_get_error( ), NULL));

        goto end;

}



BIO *p7bio = PKCS7_dataInit(p7, NULL);



if (!p7bio) {

        printf("OpenSSL Error: %s\n", ERR_error_string(ERR_get_error( ), NULL));

        goto end;

}



//Pump data to special PKCS7 BIO. This encrypts and signs it.

BIO_write(p7bio, data, strlen(data));

BIO_flush(p7bio);

PKCS7_dataFinal(p7, p7bio);                



//Write PEM encoded PKCS7

BIO *bio = BIO_new(BIO_s_mem( ));



if (!bio || (PEM_write_bio_PKCS7(bio, p7) == 0)) {

        printf("Fatal Error: Failed to create PKCS7 PEM\n");

}



BIO_flush(bio);                



char *str;

int len = BIO_get_mem_data(bio, &str);



char *ret = new char [len + 1];

memcpy(ret, str, len);

ret[len] = 0;



' free the resources:

PKCS7_free(p7);

BIO_free_all(bio);

BIO_free_all(p7bio);

The last step to enable button encryption is to change the value of the cmd form tag to _s-xclick and add the PKCS7 blob as a form value of encrypted..

When you're done, you'll end up with something like this:

<form method="post" action="https://www.sandbox.paypal.com/cgi-bin/webscr">

<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but23.gif" 

name="submit" alt="Make payments with PayPal - it's fast, free and secure!">

<input type="hidden" name="cmd" value="_s-xclick">

<input type="hidden" id="encrypted" name="encrypted" value="-----BEGIN PKCS7-----

MIIEvQYJKoZIhvcNAQcEoIIErjCCBKoCAQExggE0MIIBMAIBADCBmDCBkjELMAkG

A1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQw

EgYDVQQKEwtQYXlQYWwgSW5jLjEVMBMGA1UECxQMc3RhZ2UyX2NlcnRzMRMwEQYD

VQQDFApzdGFnZTJfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tAgEA

MA0GCSqGSIb3DQEBAQUABIGACgshgqbB147NFGZlK23kRLaQ3EkGnFmnRWn8euqN

Ecm12daiK57CaU/L36dhc4PtkigXI2TQ/alWglyerZkOhl+qb6ZRTqEq2+7fhvsB

T32Yph/usVQEj5j0njtFmo9smOyEJuHcNYY5bn3gUsiM6FxIZq8qRlI5W9yh7hTc

1/kxCzAJBgUrDgMCGgUAMGsGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQINNLmCVHP

OUWASIMAdhSkOjW5qKb98fpT1yLCByYMjvE0U39fuG3pSOXv8tKzKEz3v1sKDUOR

PRy0ekPFI6nEdp+dDJLBy3acM3DGrHk7KdYSLqCCAdIwggHOMIIBN6ADAgECAgEC

MA0GCSqGSIb3DQEBBQUAMBExDzANBgNVBAMTBlBheXBhbDAeFw0wNDAzMjkyMTU3

NDdaFw0xNDAzMjcyMTU3NDdaMBExDzANBgNVBAMTBlBheXBhbDCBnzANBgkqhkiG

9w0BAQEFAAOBjQAwgYkCgYEArdX6/kaw/9JWyxedVUBf1hLQ0nE3Z8HZTOAb8tTj

tH3anE8lxoA84NBKgsnAfsWSivWZA149NcpNrVgk7aPiCpIlxxLD7dv30zSqrXUA

kzVZ3xDfxILN42Xe8JZiM7MieixlKL/2RlnqHv6RyfAJyXH7cMlbLQJCBR3g4XnF

7I0CAwEAAaM2MDQwDgYDVR0PAQH/BAQDAgGmMA8GA1UdEwEB/wQFMAMBAf8wEQYJ

YIZIAYb4QgEBBAQDAgIEMA0GCSqGSIb3DQEBBQUAA4GBAD0CbksayWCC0yqZSn3c

6J65Yvmi/KrObGX7EzHcB1N0/YbfYkisw5qvZnGUhMj00DL3cvNOnPxXNBIUdHT3

UF1O8MzLlv8fTAjnS8Zd83vZfSyi6TMSPJlXbx8p+P2IbRNKdQaIHz2tR6tCnUNC

JYYKim3Nkz48sk0/jGtjiJPVMYIBGzCCARcCAQEwFjARMQ8wDQYDVQQDEwZQYXlw

YWwCAQIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ

KoZIhvcNAQkFMQ8XDTA0MDQyMjIxMDkyMVowIwYJKoZIhvcNAQkEMRYEFO1Oou9z

6VXvxn6wow7yZXlP6vqeMA0GCSqGSIb3DQEBAQUABIGAoNU5uAeD+pp2bROOfhHh

6oTPZDjhUvKLrhVaHmpHzz1aZTtIdqYcwZ6vEVai6fGG43hqoZYAh97xWDiwW9Ie

X/RtAzc38Yk2vch6ocPF8MjsEMVne3J9iy0rN6A0Cby5IgkKFrrYee9eWNIec/6d

3koVvLSCBZvZV+RFYCKhA/0=

-----END PKCS7-----

"> 

</form>

Obviously, this code is nearly impossible to decipher or tamper with, making it sufficiently obfuscated.

?Michael Blanton