WordPress Security for Membership Sites: Protecting Content and Subscribers
Secure your WordPress membership site with strategies to protect premium content, subscriber data, and recurring payment information.
Membership sites must protect premium content from unauthorized access while securing subscriber data and payment information. Implementing robust security measures is essential for maintaining member trust and revenue.
Membership Site Security Challenges
Membership platforms face specific threats:
- Content scraping and redistribution
- Account credential sharing
- Payment fraud and chargebacks
- Subscription manipulation
- Member data breaches
Content Protection Levels
Implement tiered content access:
function check_content_access($post_id) {
$access_level = get_post_meta($post_id, '_membership_level', true);
if (empty($access_level) || $access_level === 'public') {
return true; // Public content
}
if (!is_user_logged_in()) {
return false;
}
$user_level = get_user_membership_level(get_current_user_id());
$level_hierarchy = array(
'free' => 1,
'basic' => 2,
'premium' => 3,
'vip' => 4,
);
$required = $level_hierarchy[$access_level] ?? 0;
$user_has = $level_hierarchy[$user_level] ?? 0;
return $user_has >= $required;
}
add_filter('the_content', function($content) {
if (!is_singular()) {
return $content;
}
if (!check_content_access(get_the_ID())) {
return get_membership_teaser(get_the_ID());
}
return $content;
});
Subscription Validation
Verify active subscriptions before granting access:
function has_active_subscription($user_id) {
$subscription = get_user_subscription($user_id);
if (!$subscription) {
return false;
}
// Check expiration
if ($subscription['expires_at'] && strtotime($subscription['expires_at']) < time()) {
return false;
}
// Check status
if (!in_array($subscription['status'], array('active', 'trialing'))) {
return false;
}
// Check payment status
if ($subscription['payment_failed']) {
$grace_period = get_option('membership_grace_period', 7);
$failed_at = strtotime($subscription['payment_failed_at']);
if ((time() - $failed_at) > ($grace_period * 86400)) {
return false;
}
}
return true;
}
// Cache subscription check for performance
function get_user_membership_status($user_id) {
$cache_key = 'membership_status_' . $user_id;
$status = wp_cache_get($cache_key);
if ($status === false) {
$status = has_active_subscription($user_id);
wp_cache_set($cache_key, $status, '', 300); // 5 minute cache
}
return $status;
}
Preventing Account Sharing
Detect and limit credential sharing:
function enforce_session_limits($user_id) {
$max_sessions = get_membership_session_limit($user_id);
$sessions = WP_Session_Tokens::get_instance($user_id);
$all_sessions = $sessions->get_all();
if (count($all_sessions) > $max_sessions) {
// Keep newest sessions, destroy oldest
uasort($all_sessions, function($a, $b) {
return $b['login'] - $a['login'];
});
$to_keep = array_slice($all_sessions, 0, $max_sessions, true);
$to_destroy = array_diff_key($all_sessions, $to_keep);
foreach (array_keys($to_destroy) as $token) {
$sessions->destroy($token);
}
}
}
// Track login patterns
function analyze_login_patterns($user_id) {
global $wpdb;
$logins = $wpdb->get_results($wpdb->prepare(
"SELECT ip_address, COUNT(*) as count,
COUNT(DISTINCT ip_address) as unique_ips
FROM membership_logins
WHERE user_id = %d
AND login_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY ip_address",
$user_id
));
$unique_ips = count($logins);
$threshold = get_option('suspicious_ip_threshold', 5);
if ($unique_ips > $threshold) {
flag_account_for_review($user_id, 'Excessive unique IPs: ' . $unique_ips);
}
}
Content Scraping Prevention
Detect and block content scrapers:
function detect_scraping_behavior() {
$ip = $_SERVER['REMOTE_ADDR'];
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
// Rate limit content views
$view_key = 'content_views_' . md5($ip);
$views = get_transient($view_key) ?: 0;
if ($views > 50) { // 50 pages per 5 minutes
log_scraping_attempt($ip, $user_agent);
wp_die('Access temporarily restricted');
}
set_transient($view_key, $views + 1, 300);
// Check for missing JavaScript execution
if (isset($_COOKIE['js_check']) && $_COOKIE['js_check'] !== get_expected_js_token()) {
log_scraping_attempt($ip, 'JS verification failed');
}
// Check for known scraper user agents
$scrapers = array('HTTrack', 'wget', 'curl', 'python-requests', 'scrapy');
foreach ($scrapers as $scraper) {
if (stripos($user_agent, $scraper) !== false) {
wp_die('Automated access not permitted');
}
}
}
add_action('template_redirect', 'detect_scraping_behavior');
Secure Member Registration
Validate and verify new member signups:
function process_member_registration($data) {
// Validate email
if (!is_email($data['email'])) {
return new WP_Error('invalid_email', 'Please enter a valid email');
}
// Check for disposable email
$email_domain = substr(strrchr($data['email'], '@'), 1);
if (is_disposable_email_domain($email_domain)) {
return new WP_Error('disposable_email', 'Disposable emails not allowed');
}
// Check for existing account
if (email_exists($data['email'])) {
// Do not reveal account existence
return new WP_Error('registration_error', 'Registration failed. Please try again.');
}
// Rate limit registrations
$ip = $_SERVER['REMOTE_ADDR'];
$reg_key = 'registrations_' . md5($ip);
$recent = get_transient($reg_key) ?: 0;
if ($recent > 3) { // 3 registrations per hour
return new WP_Error('rate_limit', 'Too many registration attempts');
}
set_transient($reg_key, $recent + 1, HOUR_IN_SECONDS);
// Create user
$user_id = wp_create_user(
sanitize_user($data['username']),
wp_generate_password(16, true),
sanitize_email($data['email'])
);
if (is_wp_error($user_id)) {
return $user_id;
}
// Send verification email
send_email_verification($user_id);
return array('user_id' => $user_id, 'status' => 'pending_verification');
}
Payment Security
Secure subscription payment handling:
function process_subscription_payment($user_id, $payment_data) {
// Verify nonce
if (!wp_verify_nonce($payment_data['nonce'], 'membership_payment')) {
return new WP_Error('invalid_nonce', 'Security check failed');
}
// Validate plan
$plan = get_membership_plan($payment_data['plan_id']);
if (!$plan || !$plan['active']) {
return new WP_Error('invalid_plan', 'Selected plan not available');
}
// Log payment attempt
log_payment_attempt($user_id, $plan['id'], $_SERVER['REMOTE_ADDR']);
// Process through Stripe (example)
try {
$subscription = create_stripe_subscription(
get_user_stripe_customer_id($user_id),
$plan['stripe_price_id']
);
// Store subscription locally
save_user_subscription($user_id, array(
'stripe_subscription_id' => $subscription->id,
'plan_id' => $plan['id'],
'status' => $subscription->status,
'current_period_end' => date('Y-m-d H:i:s', $subscription->current_period_end),
));
return array('success' => true, 'subscription_id' => $subscription->id);
} catch (Exception $e) {
log_payment_error($user_id, $e->getMessage());
return new WP_Error('payment_failed', 'Payment could not be processed');
}
}
Member Data Protection
Secure member personal information:
function export_member_data($user_id) {
// Verify request is from the user
if (get_current_user_id() !== $user_id && !current_user_can('manage_options')) {
return new WP_Error('unauthorized', 'Cannot export other member data');
}
$user = get_userdata($user_id);
$data = array(
'profile' => array(
'username' => $user->user_login,
'email' => $user->user_email,
'registered' => $user->user_registered,
),
'membership' => get_user_subscription($user_id),
'content_history' => get_user_content_history($user_id),
'payment_history' => get_masked_payment_history($user_id),
);
// Log data export
log_data_export($user_id, get_current_user_id());
return $data;
}
function delete_member_account($user_id) {
// Cancel any active subscriptions
cancel_user_subscriptions($user_id);
// Anonymize payment records
anonymize_payment_records($user_id);
// Delete personal data
delete_user_content_history($user_id);
// Delete WordPress user
require_once ABSPATH . 'wp-admin/includes/user.php';
wp_delete_user($user_id);
// Log deletion
log_account_deletion($user_id);
}
Conclusion
Membership site security requires tiered content protection, subscription validation, account sharing prevention, and robust payment security. Regular monitoring helps identify and address potential security issues before they impact members.
Written by Sarah Chen
WP Folder Shield Team