PluginProbe
Plugin Detective – Troubleshooting Conflicts / 1.2.35
Plugin Detective – Troubleshooting Conflicts v1.2.35
1.2.35 1.2.33 1.2.32 1.2.31 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2 1.2.1 1.2.10 1.2.12 1.2.13 1.2.14 1.2.16 1.2.19 1.2.20 1.2.22 1.2.23 1.2.24 All 54 releases
← All changes | troubleshoot/includes/class-auth.php +50 -47 1.1.8 → 1.2.35 View file →
@@ -41,86 +41,89 @@
41 41 public function hooks() {
42 42
43 43 }
44 44
45 - public static function get_user( $username, $password, $action ) {
46 - $username = sanitize_user($username);
47 - $password = trim($password);
45 + /**
46 + * Authenticate a username/password and require plugin-management capability.
47 + *
48 + * Every failure mode — unknown username, wrong password, or a valid login
49 + * that lacks the activate_plugins capability — returns the SAME generic
50 + * error. Distinct codes/messages here would let an unauthenticated caller
51 + * probe which usernames exist (user enumeration), so they are deliberately
52 + * collapsed into one indistinguishable response.
53 + *
54 + * @since 0.0.0
55 + *
56 + * @param string $username Raw username input.
57 + * @param string $password Raw password input.
58 + * @return WP_User|WP_Error Plugin-capable user on success, or one generic error.
59 + */
60 + public static function authenticate( $username, $password ) {
61 + $username = sanitize_user( $username );
62 + $password = trim( $password );
48 63
49 64 $user = apply_filters( 'authenticate', null, $username, $password );
50 - if ( $user == null ) {
51 - $user = new WP_Error( 'authentication_failed', __( '<strong>ERROR</strong>: Invalid username, email address or incorrect password.', 'plugin-detective' ) );
52 - }
53 - if ( is_a( $user, 'WP_Error' ) ) {
54 - return $user;
55 - }
56 65
57 - if ( !user_can( $user, 'activate_plugins' ) ) {
58 - return new WP_Error( 'permission_denied', __( '<strong>ERROR</strong>: This user does not have permission to activate/deactivate plugins', 'plugin-detective' ) );
66 + if ( ! is_a( $user, 'WP_User' ) || ! user_can( $user, 'activate_plugins' ) ) {
67 + return new WP_Error( 'authentication_failed', __( '<strong>ERROR</strong>: Authentication failed.', 'plugin-detective' ) );
59 68 }
60 69
61 - // $slug = sanitize_title( $username.sha1( DB_PASSWORD . $password ).$action );
62 - return $user->data;
70 + return $user;
63 71 }
64 72
65 - public static function get_nonce( $username, $password, $action ) {
66 - $username = sanitize_user($username);
67 - $password = trim($password);
68 -
69 - $user = apply_filters( 'authenticate', null, $username, $password );
70 - if ( $user == null ) {
71 - $user = new WP_Error( 'authentication_failed', __( '<strong>ERROR</strong>: Invalid username, email address or incorrect password.', 'plugin-detective' ) );
73 + public static function create_nonce( $action, $uid = null ) {
74 + if ( null === $uid ) {
75 + $uid = get_current_user_id();
72 76 }
73 - if ( is_a( $user, 'WP_Error' ) ) {
74 - return $user;
75 - }
77 + $uid = (int) $uid;
76 78
77 - if ( !user_can( $user, 'activate_plugins' ) ) {
78 - return new WP_Error( 'permission_denied', __( '<strong>ERROR</strong>: This user does not have permission to activate/deactivate plugins', 'plugin-detective' ) );
79 - }
80 -
81 - // $slug = sanitize_title( $username.sha1( DB_PASSWORD . $password ).$action );
82 - return self::create_nonce( $action );
83 - }
84 -
85 - public static function create_nonce( $action ) {
86 - $uid = 'api';
87 -
88 - if ( !empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
89 - $token = $_SERVER['HTTP_USER_AGENT'];
79 + if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
80 + $token = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
90 81 } else {
91 82 $token = '';
92 83 }
93 84 $i = strtotime( gmdate( 'Y-m-d' ) );
94 85
95 - return substr( sha1( DB_PASSWORD . $i . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 );
86 + // Bind the token to the user it was issued for so a low-privileged user's
87 + // nonce can never stand in for an administrator's. The uid travels with the
88 + // token (the app treats it as opaque) and is re-verified on each request.
89 + return $uid . ':' . substr( sha1( DB_PASSWORD . $i . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 );
96 90 }
97 91
98 92 public static function verify_nonce( $nonce, $action ) {
99 93 $nonce = (string) $nonce;
100 - $uid = 'api';
101 - if ( !empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
102 - $token = $_SERVER['HTTP_USER_AGENT'];
103 - } else {
104 - $token = '';
94 + if ( empty( $nonce ) ) {
95 + return false;
105 96 }
106 97
107 - if ( empty( $nonce ) ) {
98 + // Tokens are "<uid>:<hash>" — recover the uid so the hash is checked against
99 + // the user it was minted for. Returns that uid on success for the caller's
100 + // capability re-check; false otherwise.
101 + $parts = explode( ':', $nonce, 2 );
102 + if ( count( $parts ) !== 2 || '' === $parts[1] ) {
108 103 return false;
109 104 }
105 + $uid = (int) $parts[0];
106 + $provided = $parts[1];
110 107
108 + if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
109 + $token = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
110 + } else {
111 + $token = '';
112 + }
113 +
111 114 $i = strtotime( gmdate( 'Y-m-d' ) );
112 115
113 116 // Nonce generated today (gmt)
114 117 $expected = substr( sha1( DB_PASSWORD . $i . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 );
115 - if ( hash_equals( $expected, $nonce ) ) {
116 - return 1;
118 + if ( hash_equals( $expected, $provided ) ) {
119 + return $uid;
117 120 }
118 121
119 122 // Nonce generated yesterday (gmt)
120 123 $expected = substr( sha1( DB_PASSWORD . ( $i - 24*60*60 ) . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 );
121 - if ( hash_equals( $expected, $nonce ) ) {
122 - return 2;
124 + if ( hash_equals( $expected, $provided ) ) {
125 + return $uid;
123 126 }
124 127
125 128 // Invalid nonce
126 129 return false;