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 +110 -5 6.48trunk View file →
@@ -1,10 +1,82 @@
1 1 <?php
2 2 if (!defined('ABSPATH')) exit;
3 3 if (!class_exists('WPRRecover')) :
4 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 +
5 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 + );
6 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 82 $secret = WPRRecover::refreshDefaultSecret($settings);
@@ -12,20 +84,49 @@
12 84 return $secret;
13 85 }
14 86
15 87 public static function refreshDefaultSecret($settings) {
88 + $settings->deleteOption(self::$default_secret_key);
89 +
16 90 $key_details = array();
17 91 $key_details["key"] = WPRAccount::randString(32);
18 - $key_details["expires_at"] = time() + (24 * 60 * 60);
92 + $key_details["expires_at"] = time() + self::SECRET_TTL;
19 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 +
20 99 $settings->updateOption(self::$default_secret_key, $key_details);
21 100
22 101 return $key_details["key"];
23 102 }
24 103
104 + public static function connectionTag($settings) {
105 + $secret = self::getDefaultSecret($settings);
106 + if (empty($secret)) {
107 + return null;
108 + }
25 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 +
26 127 public static function deleteDefaultSecret($settings) {
27 - $settings->deleteOption(self::$default_secret_key);
128 + return $settings->deleteOption(self::$default_secret_key);
28 129 }
29 130
30 131 public static function getDefaultSecret($settings) {
31 132 $key_details = $settings->getOption(self::$default_secret_key);
@@ -49,16 +150,20 @@
49 150 return $status;
50 151 }
51 152
52 153 public static function validate($key) {
53 - return $key && strlen($key) >= 32;
154 + return is_string($key) && strlen($key) >= 32;
54 155 }
55 156
56 - public static function find($settings, $pubkey) {
157 + public static function find($settings, $pubkey, $tag = null) {
57 158 if (!self::validate($pubkey)) {
58 159 return null;
59 160 }
60 161
162 + if (!self::verifyTag($settings, $tag)) {
163 + return null;
164 + }
165 +
61 166 $secret = self::getDefaultSecret($settings);
62 167 if (!self::validate($secret)) {
63 168 return null;
64 169 }
@@ -66,5 +171,5 @@
66 171 $account = new WPRAccount($settings, $pubkey, $secret);
67 172 return $account;
68 173 }
69 174 }
70 -endif;
175 +endif;