PluginProbe
The WP Remote WordPress Plugin / trunk
The WP Remote WordPress Plugin vtrunk
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
← All changes | recover.php +145 -17 5.38trunk View file →
@@ -1,47 +1,175 @@
1 1 <?php
2 2 if (!defined('ABSPATH')) exit;
3 3 if (!class_exists('WPRRecover')) :
4 4 class WPRRecover {
5 - public static $default_secret_key = 'bvSecretKey';
6 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 +
7 79 public static function defaultSecret($settings) {
8 80 $secret = self::getDefaultSecret($settings);
9 81 if (empty($secret)) {
10 - $secret = WPRAccount::randString(32);
11 - self::updateDefaultSecret($settings, $secret);
82 + $secret = WPRRecover::refreshDefaultSecret($settings);
12 83 }
13 84 return $secret;
14 85 }
15 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 +
16 127 public static function deleteDefaultSecret($settings) {
17 - $settings->deleteOption(self::$default_secret_key);
128 + return $settings->deleteOption(self::$default_secret_key);
18 129 }
19 130
20 131 public static function getDefaultSecret($settings) {
21 - return $settings->getOption(self::$default_secret_key);
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;
22 139 }
23 140
24 - public static function updateDefaultSecret($settings, $secret) {
25 - $settings->updateOption(self::$default_secret_key, $secret);
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;
26 151 }
27 152
28 - public static function validate($pubkey) {
29 - if ($pubkey && strlen($pubkey) >= 32) {
30 - return true;
31 - } else {
32 - return false;
33 - }
153 + public static function validate($key) {
154 + return is_string($key) && strlen($key) >= 32;
34 155 }
35 156
36 - public static function find($settings, $pubkey) {
157 + public static function find($settings, $pubkey, $tag = null) {
37 158 if (!self::validate($pubkey)) {
38 159 return null;
39 160 }
161 +
162 + if (!self::verifyTag($settings, $tag)) {
163 + return null;
164 + }
165 +
40 166 $secret = self::getDefaultSecret($settings);
41 - if (!empty($secret) && (strlen($secret) >= 32)) {
42 - $account = new WPRAccount($settings, $pubkey, $secret);
167 + if (!self::validate($secret)) {
168 + return null;
43 169 }
170 +
171 + $account = new WPRAccount($settings, $pubkey, $secret);
44 172 return $account;
45 173 }
46 174 }
47 -endif;
175 +endif;