November 4, 2014 2:25 pm

How to Encrypt and save credit card info in Database with PHP

If you want to make a website where you need to store user’s credit card information then you must read this tutorial and encrypt credit card number before storing in your database. So this tutorial will show you that how you can encrypt credit cards numbers and store in database and decrypt then at the time you want to use them and prevent hackers from stealing your customers credit cards information.

How to Encrypt and save credit card info in Database with PHP

[wpdm_file id=117]

Saving credit card information in your websites database is not recommended If you do store store credit card numbers you must follow PCI guidelines.

Also Read: How to Hashing Password in PHP 5.5 with Password Hashing API

Let see how to encrypt credit card number

I am using a class from stackoverflow forum which is a very simple and easy to integrate so recommend you  guys to encrypt credit card numbers before storing in database.

Make a class file with name Encryption.class.php contents below code.

<?php
class Encryption
{
    const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
    const MODE   = MCRYPT_MODE_CBC;

    /* Cryptographic key of length 16, 24 or 32. NOT a password! */
    private $key;
    public function __construct($key) {
        $this->key = $key;
    }
}

Class created with construct now add methods to encrypt and decrypt credit card number.

public function encrypt($plaintext) {
        $ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE); //Gets the size of the IV belonging to a specific cipher/mode combination.
        $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_RANDOM); //Creates an initialization vector (IV) from a random source.
        $ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv); //Encrypts the data and returns it.
        return base64_encode($iv.$ciphertext); //Encode Base 64
    }

    public function decrypt($ciphertext) {
        $ciphertext = base64_decode($ciphertext); //Decode Base 64
        $ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE); //Gets the size of the IV belonging to a specific cipher/mode combination.
        if (strlen($ciphertext) < $ivSize) {
            throw new Exception('Missing initialization vector');
        }

        $iv = substr($ciphertext, 0, $ivSize);
        $ciphertext = substr($ciphertext, $ivSize);
        $plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv); //Decrypts the data and returns it.
        return rtrim($plaintext, "\0");
    }

There is 2 function in this class encrypt($plaintext) used to change your input to some encrypted string and decrypt($ciphertext) is used to decrypt your encrypted string into the original text.

Usage of that class:

<?php
$key = "23c34eWrg56fSdrt"; // Encryption Key
$crypt = new Encryption($key);

$number = '4356789434679645'; // your credit card number

$encrypted_string = $crypt->encrypt($number); // Encrypt your credit card number

$decrypted_string = $crypt->decrypt($encrypted_string); // Decrypt your encrypted string.

// Show Results

echo "number: $number";
echo "<br><br>";
echo "encrypted_string: $encrypted_string";
echo "<br><br>";
echo "decrypted_string: $decrypted_string";
?>

On the page you encrypt and decrypt your credit card number you need to give a key of your choice to make strong encryption.

Complete code available to download so you can download and install it easily in your website and take benefit of it.

[wpdm_file id=117]

That’s all for today I hope you guys like this simple and powerful tutorial so please don’t forget to give your feedback in comments and post your problems we love to solve your problems.

 

Author Huzoor Bux

I am Huzoor Bux from Karachi (Pakistan). I have been working as a PHP Developer from last 5+ years, and its my passion to learn new things and implement them as a practice. Basically I am a PHP developer but now days exploring more in HTML5, CSS and jQuery libraries.


Tutorial Categories:

5 responses to “How to Encrypt and save credit card info in Database with PHP”

  1. Chris says:

    Uh, no. Don’t store credit card numbers. This does not protect you in any way. Encrypting the card is one thing, then what of the key used to encrypt the card? This makes no sense, you’re encrypting the data in the database and putting the key somewhere else? Using the same key for ALL credit card numbers? The key is in some PHP file? When you read about CC breaches in the PCI security magazines, it’s nearly always because they are doing exactly this type of encryption.

    If you really need to use a CC on a regular basis, use a tokenization service. Most CC gateways provide them. It de-couples the CC number from the data you’ve stored about your transaction.

    • MnemonicCarrier says:

      Uh, kind of. You’re right about it being safer not to store the key in a plain-text PHP script somewhere. However, those who set up PCI environments properly will always place the CDE (Cardholder Data Environment) behind an API with extra access controls. This is an acceptable solution for PCI compliant infrastructure (you make it near on impossible to access boxes in the CDE other than via an API).

      Alternatively, you should (and I strongly recommend this), implement a PHP extension that acts as a “key-chain” or “key-ring” or “key-locker” (or whatever you want to call it).

      Your “fingerprinting” in such a PHP extension might look something like this:

      #ifndef __FINGERPRINT__
      #define __FINGERPRINT__

      #include
      #include

      using namespace std;

      class QFingerPrint
      {
      private:
      vector blessedMacAddresses;

      public:
      QFingerPrint();
      bool isBlessedMacAddress(const string& macAddr);
      static string getMacAddress(const string& network_interface);
      static string getCpuId();
      static string getMotherboardSerial();
      };

      #endif // __FINGERPRINT__

      From the above, you can gain a pretty unique fingerprint of a computer. You then store this fingerprint within an encrypted “whitelist”.

      You could also create an AesHelperClass to perform the actual encryption with the PHP extension (i.e. a binary, not a PHP script). The specification might look something like:

      #ifndef __AESHELPER__
      #define __AESHELPER__

      #include
      #include
      #include
      #include

      using namespace std;

      typedef vector binary_vec_t;

      // ————————————————————————————————
      // ————————————————————————————————
      class QAesHelper
      {
      private:

      binary_vec_t key;
      binary_vec_t iv;

      void handleErrors();

      public:

      unsigned char *ciphertext;
      unsigned char *decryptedtext;

      QAesHelper();
      QAesHelper(unsigned char *key, int key_len, unsigned char *iv, int iv_len,
      unsigned char *ciphertext, unsigned char *decryptedtext);
      ~QAesHelper();

      int encrypt(unsigned char *plaintext, int plaintext_len);
      int decrypt(unsigned char *ciphertext, int ciphertext_len);
      };

      #endif // __AESHELPER__

      Then in the “extension code” itself, publish your methods:

      // ——————————————————————————————-
      // ——————————————————————————————-
      static zend_function_entry MYEXT_methods[] = {
      PHP_ME(MYEXT, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
      PHP_ME(MYEXT, encrypt, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
      PHP_ME(MYEXT, decrypt, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
      {NULL, NULL, NULL}
      };

      The implementation of the encrypt method within the extension might look something like:

      // ——————————————————————————————-
      // ——————————————————————————————-
      PHP_METHOD(MYEXT, encrypt)
      {
      unsigned char *plaintext;
      unsigned char *iv;

      int plaintext_len;
      int iv_len;

      if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ss”,
      &plaintext, &plaintext_len, &iv, &iv_len) == FAILURE)
      RETURN_NULL();

      unsigned char *ciphertext = (unsigned char*)emalloc(plaintext_len + 32);
      unsigned char key[32];
      getKey(key, 32);
      QAesHelper *aes = new QAesHelper(key, sizeof(key), iv, iv_len, ciphertext, NULL);
      int ciphertext_len = aes->encrypt(plaintext, plaintext_len);
      RETVAL_STRINGL((const char*)ciphertext, ciphertext_len, 1);
      efree(ciphertext);
      delete aes;
      }

      where the getKey() function gets the key according to the fingerprint of the machine it’s running on. If the fingerprint is not in the “whitelist”, then an alternative key is returned (this is useful for development environments).

      Note that in the above MYEXT::encrypt() method that only the plaintext and iv are taken as parameters. This means that within PHP itself, the usage is:

      $encrypted = MYEXT::encrypt(“Some text to encrypt”, “some IV value”);
      $decrypted = MYEXT::encrypt(“Some text to decrypt”, “some IV value”);

      This is because the key resides within the extension (hopefully in encrypted form, which means extra safeguards are required for the KEK, or “Key Encrypting Key”), and is never available in plaintext to the PHP script.

      Even if an attacker had the binary or the source code to the extension, it’s still not possible for them to decipher the encrypted keys (because elements of the machine’s fingerprint is used to form the key).

      Also note that the IV does not have to be hidden.

      Hope that this answers your question about key management with PHP in a PCI environment. Short version: write a PHP extension to manage the key and perform the encryption. That way the key is not available to anyone who accesses the box or has full access to all source code. This is what I did for a company that wanted to achieve tier 1 PCI compliance and wanted to hide their crypto keys.

      Learn something haters…

  2. MnemonicCarrier says:

    I agree with the comments here. While what you have provided “works”, it’s quite a simplistic implementation that poses many more problems. I understand that the purpose of this article is not to address a PCI compliant solution.

    In regards to the use of mcrypt_encrypt() and mcrypt_decrypt(), another option is the openssl_encrypt() and openssl_decrypt() PHP functions. They are much faster, and easier to use:

    $message = “The quick brown fox jumps over the lazy dog”;
    $key = “ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP”;
    $iv = “1234567890123456”;

    $encrypted = openssl_encrypt($message, ‘aes-256-cbc’, $key, OPENSSL_RAW_DATA, $iv);
    $decrypted = openssl_decrypt($encrypted, ‘aes-256-cbc’, $key, OPENSSL_RAW_DATA, $iv);

  3. aesop says:

    Hi , there.
    This is very useful for me to exercise encryption.
    But i want to know how to searching the DB after saving data as encrypted.
    Mysql code was as follows,
    $sql=”SELECT AES_DECRYPT(cardnumber,’$key’) as decrypted FROM MY_table WHERE cardnumber is not null”.

    But the resulting decrypted string has blank.

Leave a Reply

Your email address will not be published. Required fields are marked *