--- a/jay-login-register/includes/jay-login-register-ajax-handler.php
+++ b/jay-login-register/includes/jay-login-register-ajax-handler.php
@@ -975,19 +975,55 @@
// الف) خواندن تنظیمات فیلدها برای تشخیص نوع آنها
$custom_fields_config = json_decode( $settings['custom_fields_global_json'] ?? '[]', true );
$fields_map = [];
- if ( is_array($custom_fields_config) ) {
+ $allowed_custom_fields = [];
+ if ( is_array($custom_fields_config) ) {
foreach ($custom_fields_config as $f) {
- $fields_map[ $f['key'] ] = $f; // آرایهای برای دسترسی سریع: 'birthdate' => {config}
+ $fields_map[ $f['key'] ] = $f;
+ if ( isset($f['key']) && !empty($f['key']) ) {
+ $allowed_custom_fields[] = sanitize_key($f['key']);
+ }
}
}
+ // 🔒 Blacklist: کلیدهای خطرناک
+ $disallowed_meta_keys = [
+ 'wp_capabilities',
+ 'wp_user_level',
+ 'admin_color',
+ 'rich_editing',
+ 'comment_shortcuts',
+ 'show_admin_bar_front',
+ 'session_tokens',
+ 'user-settings',
+ 'user-settings-time'
+ ];
+ $disallowed_meta_keys = apply_filters('jay_login_register_disallowed_meta_keys', $disallowed_meta_keys, $user_id);
+ $allowed_custom_fields = apply_filters('jay_login_register_allowed_profile_fields', $allowed_custom_fields, $user_id);
+
foreach ($_POST as $post_key => $post_value) {
- if ( strpos($post_key, 'meta_') === 0 ) {
+ if ( strpos($post_key, 'meta_') !== 0 ) continue;
$real_meta_key = substr($post_key, 5);
// باگگیری: تبدیل فاصله به آندرلاین چون در POST فضاها _ میشوند
$real_meta_key = str_replace(' ', '_', $real_meta_key); // اطمینان حاصل میکنیم
$real_meta_key = sanitize_key($real_meta_key);
-
+
+ // 🔒 Level 1: Blacklist specific dangerous keys
+ if (in_array($real_meta_key, $disallowed_meta_keys, true)) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $real_meta_key, $post_value);
+ continue;
+ }
+
+ // 🔒 Level 2: Block ALL wp_* keys (WordPress protected)
+ if (strpos($real_meta_key, 'wp_') === 0) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $real_meta_key, $post_value);
+ continue;
+ }
+
+ // ✅ Level 3: Only allow whitelisted fields
+ if (!in_array($real_meta_key, $allowed_custom_fields, true)) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $real_meta_key, $post_value);
+ continue;
+ }
if ( is_array($post_value) ) {
$sanitized_values = array_map('sanitize_text_field', wp_unslash($post_value));
update_user_meta($user_id, $real_meta_key, $sanitized_values);
@@ -995,7 +1031,7 @@
$raw_value = wp_unslash($post_value);
// استفاده از sanitize_textarea_field برای حفظ اینترها در پاراگراف
$final_value = sanitize_textarea_field($raw_value);
-
+
// بررسی تنظیمات این فیلد خاص
if ( isset($fields_map[$real_meta_key]) ) {
$field_config = $fields_map[$real_meta_key];
@@ -1014,7 +1050,7 @@
}
update_user_meta($user_id, $real_meta_key, $final_value);
- }
+
}
}
--- a/jay-login-register/includes/jay-login-register-inline-handler.php
+++ b/jay-login-register/includes/jay-login-register-inline-handler.php
@@ -237,10 +237,6 @@
// ب) ادامه روال عادی (اگر کپچا درست بود)
// -----------------------------------------------------------
-// -----------------------------------------------------------
- // ب) ادامه روال عادی (اگر کپچا درست بود)
- // -----------------------------------------------------------
-
jay_login_register_inline_check_lockout_and_die($user_input_raw, $user_ip, $settings);
// 1. دریافت تنظیمات
@@ -424,15 +420,7 @@
wp_send_json_success(['html' => $otp_form_html, 'validity_period' => $validity_period * 60]);
}
-/**
- * HTML انتخاب روش
- */
-/**
- * HTML انتخاب روش دریافت کد
- */
-/**
- * HTML انتخاب روش دریافت کد
- */
+
/**
* HTML انتخاب روش دریافت کد
* این تابع با گرفتن اطلاعات کامل، دکمههای مناسب را میسازد
@@ -769,21 +757,65 @@
'display_name' => "$first_name $last_name"
]);
}
-
+ // ب) ذخیره متاها با حفاظت سهلایه (CVE-2025-15100)
+
+ // 🔒 Blacklist: کلیدهای خطرناک
+ $disallowed_meta_keys = [
+ 'wp_capabilities',
+ 'wp_user_level',
+ 'admin_color',
+ 'rich_editing',
+ 'comment_shortcuts',
+ 'show_admin_bar_front',
+ 'session_tokens',
+ 'user-settings',
+ 'user-settings-time'
+ ];
+ $disallowed_meta_keys = apply_filters('jay_login_register_disallowed_meta_keys', $disallowed_meta_keys, $user_id);
+
+ // ✅ Whitelist: فقط فیلدهای تنظیم شده در fields_config
+ $allowed_custom_fields = [];
+ if (is_array($fields_config)) {
+ foreach ($fields_config as $f) {
+ if (isset($f['key']) && !empty($f['key'])) {
+ $allowed_custom_fields[] = sanitize_key($f['key']);
+ }
+ }
+ }
+ $allowed_custom_fields = apply_filters('jay_login_register_allowed_profile_fields', $allowed_custom_fields, $user_id);
+
// ب) ذخیره متاها
foreach ($_POST as $key => $value) {
- if (strpos($key, 'meta_') === 0) {
- $meta_key = sanitize_key(substr($key, 5));
- $field_cfg = $fields_map[$meta_key] ?? null;
-
- if (is_array($value)) {
- $sanitized_values = array_map('sanitize_text_field', wp_unslash($value));
- update_user_meta($user_id, $meta_key, $sanitized_values);
- }
- else {
- $raw_val = wp_unslash($value);
- // برای پاراگراف اینترها حفظ شود
- $sanitized_value = sanitize_textarea_field($raw_val);
+ if (strpos($key, 'meta_') !== 0) continue;
+
+ $meta_key = sanitize_key(substr($key, 5));
+ $field_cfg = $fields_map[$meta_key] ?? null;
+
+ // 🔒 Level 1: Blacklist specific dangerous keys
+ if (in_array($meta_key, $disallowed_meta_keys, true)) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $meta_key, $value);
+ continue;
+ }
+
+ // 🔒 Level 2: Block ALL wp_* keys (WordPress protected)
+ if (strpos($meta_key, 'wp_') === 0) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $meta_key, $value);
+ continue;
+ }
+
+ // ✅ Level 3: Only allow whitelisted fields
+ if (!in_array($meta_key, $allowed_custom_fields, true)) {
+ do_action('jay_login_register_suspicious_meta_update', $user_id, $meta_key, $value);
+ continue;
+ }
+ if (is_array($value)) {
+ $sanitized_values = array_map('sanitize_text_field', wp_unslash($value));
+ update_user_meta($user_id, $meta_key, $sanitized_values);
+ }
+ else {
+ $raw_val = wp_unslash($value);
+ // برای پاراگراف اینترها حفظ شود
+ $sanitized_value = sanitize_textarea_field($raw_val);
if ($field_cfg) {
// تبدیل شماره به انگلیسی
@@ -796,7 +828,7 @@
}
}
update_user_meta($user_id, $meta_key, $sanitized_value);
- }
+
}
}
// 3. پاک کردن توکن امنیتی موقت (حالا که کار تمام شد)
--- a/jay-login-register/includes/user-panel/jay-login-register-ajax-handler-user-panel.php
+++ b/jay-login-register/includes/user-panel/jay-login-register-ajax-handler-user-panel.php
@@ -305,8 +305,9 @@
$body = "کد تایید شما برای تغییر ایمیل:nn" . $otp;
$headers = ['Content-Type: text/html; charset=UTF-8'];
- $sent = wp_mail($current_email, $subject, nl2br($body), $headers);
-
+ $html_body = nl2br(esc_html($body));
+ $sent = wp_mail($current_email, $subject, $html_body, $headers);
+
if (!$sent) {
$error_msg = 'خطا در ارسال ایمیل.';
if (isset($GLOBALS['jay_relog_mail_error'])) {
@@ -430,7 +431,8 @@
$body = "کد تایید برای ایمیل جدید:nn" . $otp;
$headers = ['Content-Type: text/html; charset=UTF-8'];
- if ( ! wp_mail($new_email, $subject, nl2br($body), $headers) ) {
+ $html_body = nl2br(esc_html($body));
+ if ( ! wp_mail($new_email, $subject, $html_body, $headers) ) {
wp_send_json_error(['message' => 'خطا در ارسال ایمیل.']);
}
@@ -620,11 +622,12 @@
function jay_panel_ajax_update_profile() {
check_ajax_referer('jay_login_register_nonce_action', 'nonce');
if ( ! is_user_logged_in() ) wp_send_json_error(['message' => 'دسترسی غیرمجاز.']);
+ $user_id = get_current_user_id();
+
$can_edit = get_user_meta( $user_id, 'jay_can_edit_profile', true );
if ( $can_edit === '0' ) {
wp_send_json_error(['message' => 'شما اجازه ویرایش پروفایل خود را ندارید.']);
}
- $user_id = get_current_user_id();
$user = get_userdata($user_id);
$settings = get_option( 'jay_login_register_user_panel_settings', [] );
@@ -896,39 +899,89 @@
elseif ($pass_to_save) update_user_meta($user_id, 'gozarname', $pass_to_save);
// 4. ذخیره فیلدهای سفارشی (این حلقه از بالا به اینجا منتقل شد) ✅
- foreach ($_POST as $key => $value) {
- if (strpos($key, 'jay_panel_meta_') === 0) {
- $meta_key = substr($key, 15); // طول 'jay_panel_meta_'
- $meta_key = sanitize_key($meta_key);
-
- if (is_array($value)) {
- // برای آرایهها (مثل چکباکس چندتایی) معمولاً اینتر مهم نیست اما این تابع امنتر است
- $sanitized_value = array_map('sanitize_textarea_field', wp_unslash($value));
- update_user_meta($user_id, $meta_key, $sanitized_value);
- } else {
+// ===========================================================
+// 6. ذخیره فیلدهای سفارشی با لیست سفید امن
+// ===========================================================
+
+// 6.1 ایجاد لیست سفید از فیلدهای مجاز
+$allowed_custom_fields = [];
+
+// 6.2 فقط فیلدهایی که در تنظیمات پنل کاربری تعریف شدهاند مجازند
+if (is_array($custom_fields)) {
+ foreach ($custom_fields as $field) {
+ if (isset($field['key']) && !empty($field['key'])) {
+ $key = sanitize_key($field['key']);
+ $allowed_custom_fields[] = $key;
+ }
+ }
+}
+
+// 6.3 افزودن فیلدهای سیستمی مجاز (در صورت نیاز)
+ $allowed_custom_fields = array_unique($allowed_custom_fields);
+
+ // 6.4 هوک برای توسعهدهندگان - این رو اضافه کن
+ $allowed_custom_fields = apply_filters('jay_login_register_allowed_profile_fields', $allowed_custom_fields, $user_id);
+
+// 6.4 مسدود کردن کلیدهای حساس وردپرس (Blacklist مهم)
+$disallowed_meta_keys = [
+ 'wp_capabilities',
+ 'wp_user_level',
+ 'admin_color',
+ 'rich_editing',
+ 'comment_shortcuts',
+ 'show_admin_bar_front',
+ 'session_tokens',
+ 'user-settings',
+ 'user-settings-time'
+];
+ // 6.6 هوک برای لیست غیرمجاز - این رو اضافه کن
+ $disallowed_meta_keys = apply_filters('jay_login_register_disallowed_meta_keys', $disallowed_meta_keys, $user_id);
+
+// 6.5 همچنین مسدود کردن هر کلیدی که با wp_ شروع میشود
+foreach ($_POST as $key => $value) {
+ if (strpos($key, 'jay_panel_meta_') === 0) {
+ $meta_key = substr($key, 15);
+ $meta_key = sanitize_key($meta_key);
+
+ // بررسی 1: مسدود کردن کلیدهای حساس
+ if (in_array($meta_key, $disallowed_meta_keys, true)) {
+ continue; // رد کردن این فیلد
+ }
+
+ // بررسی 2: مسدود کردن کلیدهایی که با wp_ شروع میشوند
+ if (strpos($meta_key, 'wp_') === 0) {
+ continue; // رد کردن این فیلد
+ }
+
+ // بررسی 3: فقط کلیدهای موجود در لیست سفید مجازند
+ if (!in_array($meta_key, $allowed_custom_fields, true)) {
+ continue; // رد کردن این فیلد
+ }
+
+ // 6.6 اعتبارسنجی و ذخیره ایمن
+ if (is_array($value)) {
+ $sanitized_value = array_map('sanitize_textarea_field', wp_unslash($value));
+ } else {
$sanitized_value = sanitize_text_field(wp_unslash($value));
-
- // چک کردن اینکه آیا این فیلد از نوع "شماره" است؟
- // باید آرایه فیلدها را جستجو کنیم. برای سرعت، فرض میکنیم اگر فقط عدد بود نرمالایز شود
- // یا بهتر: دوباره از کانفیگ چک کنیم (کد زیر دقیقتر است)
-
- $is_number_field = false;
- foreach ($custom_fields as $cf) {
- if ($cf['key'] === $meta_key && isset($cf['type']) && $cf['type'] === 'number') {
- $is_number_field = true;
- break;
- }
- }
-
- if ($is_number_field) {
- $sanitized_value = jay_login_register_normalize_numbers($sanitized_value);
+
+ // برای فیلدهای عددی، اعداد فارسی به انگلیسی تبدیل شوند
+ $is_number_field = false;
+ foreach ($custom_fields as $cf) {
+ if (isset($cf['key']) && $cf['key'] === $meta_key && isset($cf['type']) && $cf['type'] === 'number') {
+ $is_number_field = true;
+ break;
}
-
- update_user_meta($user_id, $meta_key, $sanitized_value);
+ }
+
+ if ($is_number_field) {
+ $sanitized_value = jay_login_register_normalize_numbers($sanitized_value);
}
}
+
+ // 6.7 ذخیره نهایی
+ update_user_meta($user_id, $meta_key, $sanitized_value);
}
-
+}
// --- Developer Hook: Action after profile update ---
do_action( 'jay_login_register_after_profile_update', $user_id, $_POST );
wp_send_json_success(['message' => 'تغییرات با موفقیت ذخیره شد.', 'redirect' => true]);
--- a/jay-login-register/jay-login-register.php
+++ b/jay-login-register/jay-login-register.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: JAY Login & Register
* Description: All-in-One Mobile OTP Login, Registration & Content Restriction plugin. Supports SMS, Email, Google, Digits & WooCommerce with Inline Forms.
- * Version: 2.6.03
+ * Version: 2.6.04
* Author: Jayarsiech
* Author URI: https://instagram.com/jayarsiech
* License: GPL v2 or later
@@ -10,12 +10,12 @@
* Text Domain: jay-login-register
* Plugin Slug: jay-login-register
* Domain Path: /languages
- */
+ */
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) exit;
-define( 'JAY_LOGIN_REGISTER_VERSION', '2.6.03' );
+define( 'JAY_LOGIN_REGISTER_VERSION', '2.6.04' );
define( 'JAY_LOGIN_REGISTER_PATH', plugin_dir_path( __FILE__ ) );
define( 'JAY_LOGIN_REGISTER_URL', plugin_dir_url( __FILE__ ) );