Client-Side Encryption

Update (5/2017): We recommend integrating with Braintree Direct instead of with Client-side Encryption. For more information, check out our website.

We, Braintree and LivingSocial, are excited about working with each other and are particularly enthused about a payments innovation we’ve mutually come up with. Aaron Batalion, CTO of LivingSocial, deserves most of the credit. We’re calling it Client-side Encryption, and in this post we’ll tell you about what it is, how it works and how you can use it to make payments more secure and flexible in addition to reducing PCI compliance scope.

What it is

Client-side is a solution that combines the best of Braintree’s traditional Server-to-Server (S2S) approach and the innovative Transparent Redirect (TR) solution. First, let’s briefly talk about how S2S and TR work.

Server-to-Server

S2S works in a similar fashion to accessing any API over HTTP. First, you create a form on your website where the user enters their credit card data, billing information, etc. When the user submits the form, the contents are sent to your server. Using the data you’ve received, you make an API call to Braintree using one of their client libraries, check the result and display necessary information to your user.

This approach is simple and flexible. You can perform your own validations on the data you’ve received from the user as well as performing any necessary data formatting. However, S2S has a serious drawback. While you may not be storing any credit card data, the simple act of that data passing through your system creates a security risk and complicates the steps necessary for you to achieve and maintain PCI compliance.

Transparent Redirect

Braintree’s other solution, Transparent Redirect, helps increase security and drastically reduces PCI compliance scope. Here’s how it works.

You start with the same form described in the S2S example, but with one big difference. Rather than submitting this form back to your system, it is submitted directly to Braintree. Braintree saves the contents of the form in their secure systems and then sends a redirect to your user’s browser. This redirects the user back to a pre-determined page on your site and the redirect includes a token in the URL’s query string.

Using the Braintree client library the token is extracted from the URL, a call is made to Braintree’s systems which completes the action started by the user’s form submission, and the result is returned to your server. You can then display the necessary information to your user regarding the success or failure of the charge. Because TR prevents any sensitive credit card data from passing through your system, it’s more secure and it becomes much easier to achieve PCI compliance.

TR does have some disadvantages. It is difficult to perform custom validations on the form data because it never passes through your system. While some validations can be performed with JavaScript, more complicated validations often require server-side information which is not available when performing TR requests. It also makes it more challenging to have a complex form, such as combining sign-up and payment information on a single page.

Client-side Encryption

Client-side works a lot like S2S in that you have a form where the user enters their credit card data, the form is posted to your server, and then you then send the data to Braintree and display the result to your user. Client-side adds a little magic into this process right after the user begins the form submission.

Once the user presses the “Submit” button on the form containing their credit card information, you use a Braintree-provided JavaScript library to encrypt sensitive fields before the form is ever posted to your server. The sensitive non-encrypted data is not included on the form that is submitted and, therefore, will never pass through your system. However, all other fields will be available to you. This means you can perform custom validations, formatting, and logging to your heart’s content without exposing yourself to the security risk and extra PCI compliance requirements of having unencrypted credit card data passing through your environment.

Using Client-side encryption, LivingSocial maintains the flexibility of processing credit card asynchronously, whenever is most convenient and accommodating to the purchase work flow. This was particularly important during LivingSocial’s recent record-breaking Amazon gift card offer which racked up an unbelievable 1.3 million purchases in a 24 hour period.

How it Works

In a nutshell, Client-side encrypts sensitive data using the public key (which we will refer to in code samples here as ‘YOUR_ENCRYPTION_KEY’) of an RSA keypair. Once the data reaches Braintree’s servers, the data is decrypted using the keypair’s private key.

Creating the Keypair

When Client-side encryption is enabled for your Braintree account, an RSA keypair is generated and you are given a specially formatted version of the public key. The private key, however, is never revealed to you or anyone else. This public key is used when encrypting information using the JavaScript library provided by Braintree.

Encrypting Sensitive Information

To encrypt sensitive information on your credit card form you simply include Braintree’s JavaScript library using a script tag, configure the library to use your public key, and call the encrypt method passing in the data you wish to be encrypted. Because Client-side uses RSA, a type of asymmetric encryption, you will be unable to decrypt the data you have encrypted using your public encryption key. Only Braintree will be able to decrypt these encrypted values.

Example

Let’s walk through an example of what your client side JavaScript code may look like when using Client-side encryption. In this example, we have a form with the id ‘transaction_form’. It contains two inputs we’d like to encrypt with the ids ‘transaction_credit_card_cvv’ and ‘transaction_credit_card_number’. Our example code will use jQuery and assumes the Braintree javascript library is available.

Here’s the basics of using the code.

var clientSideEncryptionKey = "YOUR_ENCRYPTION_KEY";
var braintree = Braintree.create(clientSideEncryptionKey);
var encryptedValue = braintree.encrypt("plainTextValue");  

And here’s the code for a complete solution. We’re using an approach of copying the form data to another hidden form. The sensitive data could be encrypted in place, but the user may see their form values change. Copying to another form prevents this behavior and makes the encryption transparent to your customers.

var clientSideEncryptionKey = "YOUR_ENCRYPTION_KEY";
var braintree = Braintree.create(clientSideEncryptionKey);
var updateIds = function (form) {  
  form.find("*").each(function (index, element) {    
    if ($(element).attr("id")) {      
      $(element).attr("id", "new_" + $(element).attr("id"));    
    }  
  });
};
var shouldEncrypt = function (element) {  
  var encryptedFields = ['transaction_credit_card_cvv', 'transaction_credit_card_number'];  
  var elementId = $(element).attr("id")  
  return ($.inArray(elementId, encryptedFields) >= 0);
};
var encryptFormValues = function () {  
  $("#transaction_form :input").each(function (index, element) {    
    var elementId = $(element).attr("id");    
    var elementValue = $(element).val();    
    if (shouldEncrypt(element)) {      
      $("#new_" + elementId).val(braintree.encrypt(elementValue));    
    } else {      
      $("#new_" + elementId).val(elementValue);    
    }  
  });
};
var encryptedForm = function () {  
  var newForm = $("#form_content").clone();  
  updateIds(newForm);  
  $('#new_form_content').append(newForm);  
  encryptFormValues();  
  return $('#new_transaction_form');
};
$('#transaction_form').submit(function () {  
  encryptedForm().submit();  
  return false;
});  

Let’s breakdown what’s happening here. First, we instantiate the Braintree object by passing our encryption_key.

var clientSideEncryptionKey = "YOUR_ENCRYPTION_KEY";var braintree = Braintree.create(clientSideEncryptionKey);  

Next, let’s look at what happens when we submit the transaction_form.

$('#transaction_form').submit(function () {  
  encryptedForm().submit();  
  return false;
});  

When the transaction_form is submitted, we create and submit an encrypted version of the form. Notice that we return false at the end of the submit function to prevent the original form from being submitted.

Now we can look at how we create the encrypted form.

var encryptedForm = function () {  
  var newForm = $("#form_content").clone();  
  updateIds(newForm);  
  $('#new_form_content').append(newForm);  
  encryptFormValues();  
  return $('#new_transaction_form');
};  

We first clone the existing transaction_form by cloning the form_content div that wraps it. After cloning, we update the ids in the form to preserve the uniqueness of the ids on the page by prepending ‘new_’ to every id. Next, we append the cloned form with the updated ids into an empty hidden div with the id new_form_content. Here’s how new_form_content looks in the html.

<div id="new_form_content" ></div>  

After the cloned form has been inserted into the page, we call the encryptFormValues method. This is where the real meat of this example lives, so let’s look at this method’s definition now along with the helper method shouldEncrypt.

var shouldEncrypt = function (element) {  
  var encryptedFields = ['transaction_credit_card_cvv', 'transaction_credit_card_number'];  
  var elementId = $(element).attr("id");  
  return ($.inArray(elementId, encryptedFields) >= 0);
};
var encryptFormValues = function () {  
  $("#transaction_form :input").each(function (index, element) {    
    var elementId = $(element).attr("id");    
    var elementValue = $(element).val();    
    if (shouldEncrypt(element)) {      
      $("#new_" + elementId).val(braintree.encrypt(elementValue));    
    } else {      
      $("#new_" + elementId).val(elementValue);    
    }  
  });
};  

We start by iterating over every input in the original transaction_form. For each input, we ask if we should encrypt this element. If the element is included in the encryptedFields we return true, otherwise we return false. If the field should be encrypted we copy the value from transaction_form, encrypt it, and set the encrypted value as the value of the associated input in the cloned form (using the ‘new_’ prefix). If we don’t need to encrypt the field, we just copy the value from transaction_form to the new form.

After the fields have been copied to the new form, it is then submitted and the process is complete. We’ve encrypted selected fields in the client’s browser and submitted them without affecting the user’s experience.

Wrap Up

We hope that this overview of Client-side Encryption helps you understand how to add secure and flexible credit card processing to your existing applications. We think it is a next generation payment innovation that dramatically improves security, flexibility and eleminates the risks of third party reliability inherent in the payment networks.

Client-side Encryption is currently in a limited beta; e-mail us if you're interested in using it.

Update: Client-side Encryption is no longer in beta. You can start using it right away.

***
Drew Olson Drew is a Principal Engineer at Braintree. More posts by this author

You Might Also Like