Encryption Strategy for Bank Account Data

PHP

Difficulty Level:

Snippet Description

1. Encryption Helper Functions


<?php
/**
 * Encryption Helper Functions for Bank Account Data
 * Uses OpenSSL for AES-256-CBC encryption
 */

/**
 * Get encryption key from wp-config.php
 * IMPORTANT: Add this constant to your wp-config.php file
 */
function get_encryption_key() {
    if (!defined('BANK_DATA_ENCRYPTION_KEY')) {
        // Fallback - but you should define this in wp-config.php
        error_log('BANK_DATA_ENCRYPTION_KEY not defined in wp-config.php');
        return hash('sha256', AUTH_KEY . SECURE_AUTH_KEY);
    }
    return hash('sha256', BANK_DATA_ENCRYPTION_KEY);
}

/**
 * Encrypt sensitive data
 */
function encrypt_bank_data($data) {
    if (empty($data)) {
        return '';
    }
    
    $encryption_key = get_encryption_key();
    $iv_length = openssl_cipher_iv_length('aes-256-cbc');
    $iv = openssl_random_pseudo_bytes($iv_length);
    
    $encrypted = openssl_encrypt(
        $data,
        'aes-256-cbc',
        $encryption_key,
        0,
        $iv
    );
    
    // Combine IV and encrypted data, then base64 encode
    return base64_encode($iv . $encrypted);
}

/**
 * Decrypt sensitive data
 */
function decrypt_bank_data($encrypted_data) {
    if (empty($encrypted_data)) {
        return '';
    }
    
    $encryption_key = get_encryption_key();
    $data = base64_decode($encrypted_data);
    
    $iv_length = openssl_cipher_iv_length('aes-256-cbc');
    $iv = substr($data, 0, $iv_length);
    $encrypted = substr($data, $iv_length);
    
    $decrypted = openssl_decrypt(
        $encrypted,
        'aes-256-cbc',
        $encryption_key,
        0,
        $iv
    );
    
    return $decrypted;
}

/**
 * Mask bank account number for display (show last 4 digits)
 */
function mask_account_number($account_number, $show_last = 4) {
    $decrypted = decrypt_bank_data($account_number);
    
    if (strlen($decrypted) <= $show_last) {
        return str_repeat('*', strlen($decrypted));
    }
    
    return str_repeat('*', strlen($decrypted) - $show_last) . substr($decrypted, -$show_last);
}

 

2. Encrypt Data on Form Submission


<?php
/**
 * Encrypt bank account data before saving to database
 * Hooks into JetFormBuilder before form submission
 */
add_filter('jet-form-builder/form-handler/before-send', 'encrypt_bank_account_before_save', 10, 2);

function encrypt_bank_account_before_save($request, $form_id) {
    // Only apply to the bank account form (form ID 1616)
    if ($form_id != 1616) {
        return $request;
    }
    
    // Encrypt account_number if present
    if (isset($request['account_number']) && !empty($request['account_number'])) {
        $request['account_number'] = encrypt_bank_data($request['account_number']);
    }
    
    // Encrypt routing_number if present
    if (isset($request['routing_number']) && !empty($request['routing_number'])) {
        $request['routing_number'] = encrypt_bank_data($request['routing_number']);
    }
    
    return $request;
}

3. Decrypt Data When Retrieving


<?php
/**
 * Get user's bank accounts with decrypted data (for processing)
 */
function get_user_bank_accounts_decrypted($user_id = null) {
    if (!$user_id) {
        $user_id = get_current_user_id();
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'jet_cct_bank_accounts';
    
    $accounts = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM $table WHERE cct_author_id = %d AND cct_status = 'publish' ORDER BY cct_created DESC",
        $user_id
    ));
    
    // Decrypt sensitive fields
    foreach ($accounts as $account) {
        $account->account_number_decrypted = decrypt_bank_data($account->account_number);
        $account->routing_number_decrypted = decrypt_bank_data($account->routing_number);
        
        // Keep encrypted versions for display
        $account->account_number_masked = mask_account_number($account->account_number);
        $account->routing_number_masked = mask_account_number($account->routing_number, 2);
    }
    
    return $accounts;
}

/**
 * Get user's bank accounts with masked data (for display)
 */
function get_user_bank_accounts_masked($user_id = null) {
    if (!$user_id) {
        $user_id = get_current_user_id();
    }
    
    global $wpdb;
    $table = $wpdb->prefix . 'jet_cct_bank_accounts';
    
    $accounts = $wpdb->get_results($wpdb->prepare(
        "SELECT _ID, cct_author_id, account_holder_name, bank_name, account_number, routing_number, is_default, cct_created 
         FROM $table 
         WHERE cct_author_id = %d AND cct_status = 'publish' 
         ORDER BY cct_created DESC",
        $user_id
    ));
    
    // Only show masked versions
    foreach ($accounts as $account) {
        $account->account_number = mask_account_number($account->account_number);
        $account->routing_number = mask_account_number($account->routing_number, 2);
    }
    
    return $accounts;
}


 

4. Security Configuration

Add this to your wp-config.php file (above the “That’s all, stop editing!” line):

/**
 * Encryption key for sensitive bank account data
 * IMPORTANT: Generate a unique key and keep it secure
 * Never commit this to version control
 */
define('BANK_DATA_ENCRYPTION_KEY', 'your-unique-256-bit-encryption-key-here');

To generate a secure encryption key, run this PHP snippet once:

<?php
// Run this once to generate a secure key
echo bin2hex(random_bytes(32));

5. Update Validation Function

Update your validation to work with encrypted data:

<?php
/**
 * Validate bank account form (updated for encryption)
 */
add_filter('jet-form-builder/form-handler/before-send', 'validate_bank_account_form_encrypted', 5, 2);

function validate_bank_account_form_encrypted($request, $form_id) {
    if ($form_id != 1616) {
        return $request;
    }
    
    // Check if user is logged in
    if (!is_user_logged_in()) {
        wp_die('You must be logged in to add a bank account.');
    }
    
    $user_id = get_current_user_id();
    $account_number = isset($request['account_number']) ? $request['account_number'] : '';
    
    // Check for duplicate account numbers
    // Note: We need to check against all encrypted values
    global $wpdb;
    $table = $wpdb->prefix . 'jet_cct_bank_accounts';
    
    $existing_accounts = $wpdb->get_results($wpdb->prepare(
        "SELECT account_number FROM $table WHERE cct_author_id = %d AND cct_status = 'publish'",
        $user_id
    ));
    
    // Decrypt and compare each account
    foreach ($existing_accounts as $existing) {
        $decrypted_existing = decrypt_bank_data($existing->account_number);
        if ($decrypted_existing === $account_number) {
            wp_die('This account number is already registered.');
        }
    }
    
    return $request;
}

Usage Instructions

Table of Contents