PluginProbe
The WP Remote WordPress Plugin / 6.72
The WP Remote WordPress Plugin v6.72
6.72 6.69 6.65 6.62 6.48 6.47 4.87 4.97 5.05 5.09 5.16 5.22 5.24 5.25 5.38 5.41 5.42 5.45 5.47 5.53 5.56 5.65 5.68 5.72 5.73 All 53 releases
wpremote / recover.php

recover.php in The WP Remote WordPress Plugin 6.72, at recover.php

176 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit;
3 if (!class_exists('WPRRecover')) :
4 class WPRRecover {
5
6 const SECRET_TTL = 1800;
7 const TAG_LENGTH = 32;
8 const SALT_LENGTH = 64;
9 const MIN_SALT_LENGTH = 32;
10 const SALT_CONSTANT = 'AUTH_SALT';
11 const SALT_PLACEHOLDER = 'put your unique phrase here';
12
13 public static $default_secret_key = 'bv_default_secret_key';
14 private static $other_salt_constants = array(
15 'AUTH_KEY', 'SECURE_AUTH_KEY', 'SECURE_AUTH_SALT',
16 'LOGGED_IN_KEY', 'LOGGED_IN_SALT',
17 'NONCE_KEY', 'NONCE_SALT'
18 );
19
20 public static function saltMaterial($settings) {
21 $salt = self::configSalt();
22 if (!empty($salt)) {
23 return $salt;
24 }
25
26 return self::storedSalt($settings);
27 }
28
29 private static function configSalt() {
30 if (!defined(self::SALT_CONSTANT)) {
31 return null;
32 }
33
34 $value = constant(self::SALT_CONSTANT);
35 if (!is_string($value) || strlen($value) < self::MIN_SALT_LENGTH ||
36 self::isPlaceholder($value) || self::isSharedWithOtherSalts($value)) {
37 return null;
38 }
39
40 return $value;
41 }
42
43 private static function isSharedWithOtherSalts($value) {
44 foreach (self::$other_salt_constants as $constant) {
45 if (defined($constant) && constant($constant) === $value) {
46 return true;
47 }
48 }
49
50 return false;
51 }
52
53 private static function storedSalt($settings) {
54 $key_details = $settings->getOption(self::$default_secret_key);
55 if (!is_array($key_details) || !isset($key_details["salt"])) {
56 return null;
57 }
58
59 $salt = $key_details["salt"];
60 if (!is_string($salt) || strlen($salt) < self::MIN_SALT_LENGTH) {
61 return null;
62 }
63
64 return $salt;
65 }
66
67 private static function isPlaceholder($value) {
68 if ($value === self::SALT_PLACEHOLDER) {
69 return true;
70 }
71
72 #wp-config-sample.php is localized for some locales, so the placeholder
73 #is not always the English string. wp_salt() guards against the
74 #translated form the same way.
75 // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
76 return function_exists('__') && $value === __('put your unique phrase here');
77 }
78
79 public static function defaultSecret($settings) {
80 $secret = self::getDefaultSecret($settings);
81 if (empty($secret)) {
82 $secret = WPRRecover::refreshDefaultSecret($settings);
83 }
84 return $secret;
85 }
86
87 public static function refreshDefaultSecret($settings) {
88 $settings->deleteOption(self::$default_secret_key);
89
90 $key_details = array();
91 $key_details["key"] = WPRAccount::randString(32);
92 $key_details["expires_at"] = time() + self::SECRET_TTL;
93
94 #Only carried when wp-config.php has nothing usable to bind the tag to.
95 if (empty(self::configSalt())) {
96 $key_details["salt"] = WPRAccount::randString(self::SALT_LENGTH);
97 }
98
99 $settings->updateOption(self::$default_secret_key, $key_details);
100
101 return $key_details["key"];
102 }
103
104 public static function connectionTag($settings) {
105 $secret = self::getDefaultSecret($settings);
106 if (empty($secret)) {
107 return null;
108 }
109
110 $material = self::saltMaterial($settings);
111 if (empty($material)) {
112 return null;
113 }
114
115 return substr(hash_hmac('sha256', $secret, $material), 0, self::TAG_LENGTH);
116 }
117
118 public static function verifyTag($settings, $tag) {
119 $expected = self::connectionTag($settings);
120 if (empty($expected)) {
121 return false;
122 }
123
124 return is_string($tag) && hash_equals($expected, $tag);
125 }
126
127 public static function deleteDefaultSecret($settings) {
128 return $settings->deleteOption(self::$default_secret_key);
129 }
130
131 public static function getDefaultSecret($settings) {
132 $key_details = $settings->getOption(self::$default_secret_key);
133
134 if (is_array($key_details) && $key_details["expires_at"] > time()) {
135 return $key_details["key"];
136 }
137
138 return null;
139 }
140
141 public static function getSecretStatus($settings) {
142 $key_details = $settings->getOption(self::$default_secret_key);
143 $status = 'ACTIVE';
144 if (!is_array($key_details)) {
145 $status = 'DELETED';
146 } elseif ($key_details["expires_at"] <= time()) {
147 $status = 'EXPIRED';
148 }
149
150 return $status;
151 }
152
153 public static function validate($key) {
154 return is_string($key) && strlen($key) >= 32;
155 }
156
157 public static function find($settings, $pubkey, $tag = null) {
158 if (!self::validate($pubkey)) {
159 return null;
160 }
161
162 if (!self::verifyTag($settings, $tag)) {
163 return null;
164 }
165
166 $secret = self::getDefaultSecret($settings);
167 if (!self::validate($secret)) {
168 return null;
169 }
170
171 $account = new WPRAccount($settings, $pubkey, $secret);
172 return $account;
173 }
174 }
175 endif;
176