WordPress Security for E-commerce: Beyond WooCommerce Best Practices
Comprehensive e-commerce security covering Easy Digital Downloads, Shopify integrations, custom stores, and PCI compliance essentials.
E-commerce security extends beyond any single plugin. This guide covers universal security practices for WordPress online stores, regardless of the platform used.
E-commerce Security Fundamentals
All online stores must address:
- Payment data protection and PCI compliance
- Customer account security
- Order and transaction integrity
- Inventory and pricing protection
- Fraud detection and prevention
Payment Security Essentials
Never store card data locally:
// Use tokenization for all payments
function process_secure_payment($order_id, $payment_token) {
// Never process raw card numbers
if (isset($_POST['card_number'])) {
log_security_violation('Raw card data submitted');
return new WP_Error('security', 'Invalid payment method');
}
// Validate token format
if (!preg_match('/^tok_[a-zA-Z0-9]+$/', $payment_token)) {
return new WP_Error('invalid_token', 'Invalid payment token');
}
// Process with payment provider
$result = process_tokenized_payment(
$payment_token,
get_order_total($order_id),
get_order_currency($order_id)
);
// Log transaction (without sensitive data)
log_transaction(array(
'order_id' => $order_id,
'status' => $result['status'],
'transaction_id' => $result['id'],
'amount' => $result['amount'],
// Never log card numbers or CVV
));
return $result;
}
Order Integrity Protection
Prevent order manipulation:
function validate_order_submission($order_data) {
// Recalculate totals server-side
$calculated_total = 0;
foreach ($order_data['items'] as $item) {
$product = get_product($item['product_id']);
if (!$product || !$product['in_stock']) {
return new WP_Error('invalid_product', 'Product not available');
}
// Use database price, not submitted price
$price = $product['price'];
// Apply valid discounts only
if (!empty($item['discount_code'])) {
$discount = validate_discount_code($item['discount_code'], $item['product_id']);
if ($discount) {
$price = apply_discount($price, $discount);
}
}
$calculated_total += $price * intval($item['quantity']);
}
// Add shipping
$shipping = calculate_shipping(
$order_data['shipping_address'],
$order_data['shipping_method']
);
$calculated_total += $shipping;
// Add taxes
$tax = calculate_tax($calculated_total, $order_data['billing_address']);
$calculated_total += $tax;
// Compare with submitted total
if (abs($calculated_total - $order_data['total']) > 0.01) {
log_security_event(array(
'type' => 'price_manipulation',
'submitted' => $order_data['total'],
'calculated' => $calculated_total,
'ip' => $_SERVER['REMOTE_ADDR'],
));
// Use calculated total regardless
$order_data['total'] = $calculated_total;
}
return $order_data;
}
Customer Account Protection
Secure customer accounts and data:
// Require re-authentication for sensitive actions
function protect_account_actions() {
$sensitive_actions = array(
'change_email',
'change_password',
'update_payment_method',
'view_order_history',
'download_invoice',
);
$action = $_POST['action'] ?? $_GET['action'] ?? '';
if (in_array($action, $sensitive_actions)) {
$last_auth = get_user_meta(get_current_user_id(), 'last_password_entry', true);
// Require password every 15 minutes
if (!$last_auth || (time() - $last_auth) > 900) {
wp_redirect(add_query_arg('reauth', 1, wp_login_url()));
exit;
}
}
}
add_action('init', 'protect_account_actions');
// Protect customer data in exports
function sanitize_customer_export($customer_data) {
// Mask payment information
if (isset($customer_data['payment_methods'])) {
foreach ($customer_data['payment_methods'] as &$method) {
$method['card_number'] = '**** **** **** ' . substr($method['card_last4'], -4);
unset($method['card_last4']);
}
}
// Remove sensitive metadata
unset($customer_data['password_hash']);
unset($customer_data['security_questions']);
return $customer_data;
}
Inventory Security
Protect inventory and pricing data:
// Log all inventory changes
add_action('update_post_meta', function($meta_id, $post_id, $meta_key, $meta_value) {
$inventory_keys = array('_stock', '_price', '_sale_price', '_stock_status');
if (!in_array($meta_key, $inventory_keys)) {
return;
}
$old_value = get_post_meta($post_id, $meta_key, true);
global $wpdb;
$wpdb->insert('inventory_audit_log', array(
'product_id' => $post_id,
'field' => $meta_key,
'old_value' => $old_value,
'new_value' => $meta_value,
'changed_by' => get_current_user_id(),
'changed_at' => current_time('mysql'),
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
));
// Alert on significant changes
if ($meta_key === '_price') {
$change_percent = abs(($meta_value - $old_value) / $old_value * 100);
if ($change_percent > 25) {
wp_mail(
get_option('admin_email'),
'Large price change detected',
sprintf(
'Product %d price changed from %s to %s (%.1f%%)',
$post_id, $old_value, $meta_value, $change_percent
)
);
}
}
}, 10, 4);
Fraud Detection
Implement basic fraud scoring:
function calculate_fraud_score($order) {
$score = 0;
$flags = array();
// Check billing/shipping mismatch
if ($order['billing_address'] !== $order['shipping_address']) {
$score += 10;
$flags[] = 'Address mismatch';
}
// Check for high-risk countries
$high_risk_countries = array('XX', 'YY'); // Configure as needed
if (in_array($order['billing_country'], $high_risk_countries)) {
$score += 20;
$flags[] = 'High-risk country';
}
// Check order velocity
$recent_orders = get_orders_by_email($order['email'], '-24 hours');
if (count($recent_orders) > 3) {
$score += 25;
$flags[] = 'Multiple orders in 24h';
}
// Check for disposable email
if (is_disposable_email($order['email'])) {
$score += 30;
$flags[] = 'Disposable email';
}
// Check IP reputation
$ip_risk = check_ip_reputation($order['ip_address']);
if ($ip_risk > 50) {
$score += 20;
$flags[] = 'Risky IP address';
}
// Check for proxy/VPN
if (is_proxy_ip($order['ip_address'])) {
$score += 15;
$flags[] = 'Proxy/VPN detected';
}
return array(
'score' => $score,
'flags' => $flags,
'action' => $score > 50 ? 'review' : ($score > 30 ? 'flag' : 'approve'),
);
}
Digital Product Protection
Secure downloadable products:
function generate_secure_download_link($product_id, $order_id, $user_id) {
// Verify purchase
if (!user_purchased_product($user_id, $product_id, $order_id)) {
return new WP_Error('not_purchased', 'Product not purchased');
}
// Check download limits
$downloads = get_download_count($order_id, $product_id);
$limit = get_download_limit($product_id);
if ($limit > 0 && $downloads >= $limit) {
return new WP_Error('limit_exceeded', 'Download limit reached');
}
// Generate signed URL
$expires = time() + 3600; // 1 hour
$data = array(
'product' => $product_id,
'order' => $order_id,
'user' => $user_id,
'expires' => $expires,
);
$signature = hash_hmac('sha256', json_encode($data), wp_salt('auth'));
return add_query_arg(array(
'download' => base64_encode(json_encode($data)),
'sig' => $signature,
), home_url('/download/'));
}
function serve_download($download_data, $signature) {
$data = json_decode(base64_decode($download_data), true);
// Verify signature
$expected = hash_hmac('sha256', json_encode($data), wp_salt('auth'));
if (!hash_equals($expected, $signature)) {
wp_die('Invalid download link');
}
// Check expiration
if ($data['expires'] < time()) {
wp_die('Download link expired');
}
// Log download
log_download($data['product'], $data['order'], $data['user']);
// Serve file
$file_path = get_product_file_path($data['product']);
serve_file_download($file_path);
}
Conclusion
E-commerce security requires payment protection, order validation, customer account security, inventory monitoring, fraud detection, and secure digital delivery. Regular security audits ensure ongoing protection of your store and customers.
Written by Sarah Chen
WP Folder Shield Team