| @@ -28,8 +28,62 @@ | ||
| 28 | 28 | */ |
| 29 | 29 | class c_ws_plugin__s2member_register_access |
| 30 | 30 | { |
| 31 | 31 | /** |
| 32 | + * Encrypts and authenticates a registration link or cookie value. | |
| 33 | + * | |
| 34 | + * @package s2Member\Registrations | |
| 35 | + * @since 261006 | |
| 36 | + * | |
| 37 | + * @param string $string Value to encrypt. | |
| 38 | + * @param string $purpose Registration context: `link` or the cookie field name. | |
| 39 | + * @return string Authenticated encrypted token, or an empty string for an empty value. | |
| 40 | + */ | |
| 41 | + public static function encrypt_token($string, $purpose) | |
| 42 | + { | |
| 43 | + $string = is_scalar($string) ? (string)$string : ''; | |
| 44 | + if($string === '' || !is_string($purpose) || $purpose === '') | |
| 45 | + return ''; | |
| 46 | + | |
| 47 | + $encrypted = c_ws_plugin__s2member_utils_encryption::encrypt($string); | |
| 48 | + if($encrypted === '') | |
| 49 | + return ''; | |
| 50 | + $payload = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode($encrypted); | |
| 51 | + //261006.0428 Authenticate even older-runtime encryption; use a separate WordPress salt because legacy ciphertext can expose its encryption key. | |
| 52 | + $signature = hash_hmac('sha256', 's2member:registration:v1:'.$purpose.':'.$payload, wp_salt('auth')); | |
| 53 | + return 's2reg1.'.$payload.'.'.$signature; | |
| 54 | + } | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * Verifies a registration token before decrypting it. | |
| 58 | + * | |
| 59 | + * @package s2Member\Registrations | |
| 60 | + * @since 261006 | |
| 61 | + * | |
| 62 | + * @param string $token Encrypted registration token. | |
| 63 | + * @param string $purpose Expected registration context: `link` or the cookie field name. | |
| 64 | + * @return string Verified plaintext, or an empty string for an invalid token. | |
| 65 | + */ | |
| 66 | + public static function decrypt_token($token, $purpose) | |
| 67 | + { | |
| 68 | + if(!is_string($token) || $token === '' || !is_string($purpose) || $purpose === '') | |
| 69 | + return ''; | |
| 70 | + | |
| 71 | + if(strpos($token, 's2reg1.') === 0) | |
| 72 | + { | |
| 73 | + $parts = explode('.', $token); | |
| 74 | + if(count($parts) !== 3 || $parts[1] === '' || !preg_match('/^[a-f0-9]{64}$/D', $parts[2])) | |
| 75 | + return ''; | |
| 76 | + $signature = hash_hmac('sha256', 's2member:registration:v1:'.$purpose.':'.$parts[1], wp_salt('auth')); | |
| 77 | + if(!hash_equals($signature, $parts[2])) | |
| 78 | + return ''; | |
| 79 | + return c_ws_plugin__s2member_utils_encryption::decrypt(c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($parts[1])); | |
| 80 | + } | |
| 81 | + //261006.0428 Existing authenticated Defuse registrations remain valid; unsigned legacy registrations must never reach the fallback decryptor. | |
| 82 | + return c_ws_plugin__s2member_utils_defuse::decrypt($token); | |
| 83 | + } | |
| 84 | + | |
| 85 | + /** | |
| 32 | 86 | * Generates Registration Access Links. |
| 33 | 87 | * |
| 34 | 88 | * @package s2Member\Registrations |
| 35 | 89 | * @since 3.5 |
| @@ -49,9 +103,9 @@ | ||
| 49 | 103 | unset($__refs, $__v); |
| 50 | 104 | |
| 51 | 105 | if($subscr_gateway && is_string($subscr_gateway) && $subscr_id && is_string($subscr_id) && $custom && is_string($custom) && $item_number && (is_string($item_number) || is_numeric($item_number))) |
| 52 | 106 | { |
| 53 | - $register = c_ws_plugin__s2member_utils_encryption::encrypt('subscr_gateway_subscr_id_custom_item_number_time:.:|:.:'.$subscr_gateway.':.:|:.:'.$subscr_id.':.:|:.:'.$custom.':.:|:.:'.$item_number.':.:|:.:'.strtotime('now')); | |
| 107 | + $register = self::encrypt_token('subscr_gateway_subscr_id_custom_item_number_time:.:|:.:'.$subscr_gateway.':.:|:.:'.$subscr_id.':.:|:.:'.$custom.':.:|:.:'.$item_number.':.:|:.:'.strtotime('now'), 'link'); | |
| 54 | 108 | |
| 55 | 109 | $register_link = home_url('/?s2member_register='.urlencode($register)); // Generate long URL/link. |
| 56 | 110 | $now = strtotime('now'); |
| 57 | 111 | $register_link_exp_time = apply_filters('ws_plugin__s2member_register_link_exp_time', '2 days', get_defined_vars()); |
| @@ -112,9 +166,9 @@ | ||
| 112 | 166 | |
| 113 | 167 | do_action('ws_plugin__s2member_before_reg_cookies_ok', get_defined_vars()); |
| 114 | 168 | |
| 115 | 169 | if(isset ($_COOKIE['s2member_subscr_gateway'], $_COOKIE['s2member_subscr_id'], $_COOKIE['s2member_custom'], $_COOKIE['s2member_item_number'])) |
| 116 | - if(($subscr_gateway = c_ws_plugin__s2member_utils_encryption::decrypt((string)$_COOKIE['s2member_subscr_gateway'])) && ($subscr_id = c_ws_plugin__s2member_utils_encryption::decrypt((string)$_COOKIE['s2member_subscr_id'])) && preg_match('/^'.preg_quote(preg_replace('/\:([0-9]+)$/', '', $_SERVER['HTTP_HOST']), '/').'/i', ($custom = c_ws_plugin__s2member_utils_encryption::decrypt((string)$_COOKIE['s2member_custom']))) && preg_match($GLOBALS['WS_PLUGIN__']['s2member']['c']['membership_item_number_w_level_regex'], ($item_number = c_ws_plugin__s2member_utils_encryption::decrypt((string)$_COOKIE['s2member_item_number']))) && !$wpdb->get_var("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE `meta_key` = '".$wpdb->prefix."s2member_subscr_id' AND `meta_value` = '".esc_sql($subscr_id)."' LIMIT 1")) | |
| 170 | + if(($subscr_gateway = self::decrypt_token((string)$_COOKIE['s2member_subscr_gateway'], 'subscr_gateway')) && ($subscr_id = self::decrypt_token((string)$_COOKIE['s2member_subscr_id'], 'subscr_id')) && preg_match('/^'.preg_quote(preg_replace('/\:([0-9]+)$/', '', $_SERVER['HTTP_HOST']), '/').'/i', ($custom = self::decrypt_token((string)$_COOKIE['s2member_custom'], 'custom'))) && preg_match($GLOBALS['WS_PLUGIN__']['s2member']['c']['membership_item_number_w_level_regex'], ($item_number = self::decrypt_token((string)$_COOKIE['s2member_item_number'], 'item_number'))) && !$wpdb->get_var("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE `meta_key` = '".$wpdb->prefix."s2member_subscr_id' AND `meta_value` = '".esc_sql($subscr_id)."' LIMIT 1")) | |
| 117 | 171 | $reg_cookies_ok = $reg_cookies = array('subscr_gateway' => $subscr_gateway, 'subscr_id' => $subscr_id, 'custom' => $custom, 'item_number' => $item_number); |
| 118 | 172 | |
| 119 | 173 | return apply_filters('ws_plugin__s2member_reg_cookies_ok', !empty($reg_cookies_ok) && !empty($reg_cookies) ? $reg_cookies : FALSE, get_defined_vars()); |
| 120 | 174 | } |