And Still Deploy Multiple Times a Day
A Brief History of Saving Credit Card Numbers at ActBlue
We take payments, it’s what we do. Thus, when donors ask us to, we save their credit card numbers. Saving payment information for our awesome users is immensely important to how well we operate, as we’ve mentioned many times before. Our site is a large Rails app so naturally our initial approach was to save the card numbers1 in our main database alongside the rest of the data we need for each card.2
We put the upmost value on security across our entire site, but credit cards are obviously the biggest target and require the most protection. A few years back we were in the process of upgrading to the highest level of PCI3 compliance. Several important changes came with this. Our server infrastructure would need to be certified and contain very strict levels of scrutiny: ultra-restrictive firewalls, intrusion detection, and the like. We would also need to put into place an extra layer of procedures for code changes. We want to make sure that developers are constantly thinking through and documenting all possible ramifications of the changes they are making. Other people then review and approve the new work.
This level of security around credit cards is very important, but we also strive to be agile4 in our development practices. We’re accustomed to deploying site updates multiple times per day in order to get improvements into the hards of our users just as fast as possible. We didn’t want to drag down all our work with extra process and overhead5 when just the credit card number storage requires PCI compliance.
The New Way
In the end we decided to split off a separate service just for the storage of credit cards. This app lives on its own ultra-secure servers and passes tokens back to the main site to save in the place of credit card numbers (which the main app never sees).
How It Works
A diagram of our credit card number storage system.
Structure of the Token
While we do want to keep the full credit card number out of our app, there are some structural features of a card number that are rather convenient and we’d rather not lose entirely. The first few digits of the number tell us what type of card this is—Visa, MasterCard, etc. The last four digits are a ubiquitous identifier aiding the donor and our customer service team alike in determining which card was the one used for this transaction.
We designed our tokens around these features. We make them 16 digits long to smell a bit like a credit card number. The first few digits and the last four are the same as the original card number. In the middle we stick a bunch of random letters. Using letters here gives a very straightforward indication that this is a token and not a real credit card number.
The most important benefit of this design is an increase in security. Our main app—which is necessarily large, complex, and used by lots of people—has no ability to access any credit card numbers. The application that does store them is much smaller in scope. It can be run on different servers with more lock-down and monitoring. And because it is smaller and simpler, fewer people need to have access in order to keep it up and running.7
A secondary benefit of this design comes from reduced bureaucratic overhead. Our level of PCI compliance brings with it reporting, double checking, paperwork, etc. There’s obviously a ton of good reasons for all this, but there’s also a lot of value in being nimble and shortening the time between writing code and having it running for our customers. Splitting out the code that touches credit card numbers helps us practice our due diligence properly, but just for the bits that need it. Meanwhile we’re still deploying our main application multiple times a day.
This design has been working great for us. Have you done something similar? Or faced with a similar challenge did you take a different path? Let us know, we’d love to hear about it!
1 Encrypted! ↩︎
2 Holder name, expiration date, last 4 digits of the number, etc. ↩︎
3 A set of security standards for handling credit cards defined by the credit card industry. ↩︎
4 Lower case “A”. ↩︎
5 We already have a strong culture of test coverage and code reviews for everything we touch. ↩︎
6 Usually… ↩︎
7 There’s an additional feature designed to stop theft of the database as well. The card numbers are all encrypted (of course) using an algorithm that requires several people to all enter their personal secret keys (much like a missile silo) before the running server process can decrypt anything. Any time the server restarts, the keys need to be entered again. ↩︎