'', 'secret_key' => ''); } $c = AIEO_SSO_Buttons_Data::captcha_settings(); return array( 'site_key' => isset($c['site_key']) ? (string) $c['site_key'] : '', 'secret_key' => isset($c['secret_key']) ? (string) $c['secret_key'] : '', ); } public static function expected_host() { $host = wp_parse_url(home_url(), PHP_URL_HOST); return is_string($host) ? strtolower($host) : ''; } public static function status() { $stored = get_option(self::OPTION); if (!is_array($stored) || empty($stored['fingerprint'])) { return null; } if (!hash_equals((string) $stored['fingerprint'], self::fingerprint())) { return null; } return $stored; } public static function verified_for_enforcement() { $s = self::status(); return is_array($s) && !empty($s['ok']) && isset($s['stage']) && 'full' === $s['stage']; } private static function record(array $verdict) { $verdict['fingerprint'] = self::fingerprint(); $verdict['checked_at'] = time(); update_option(self::OPTION, $verdict, false); return $verdict; } public static function forget() { delete_option(self::OPTION); } public static function verify_token($token) { $token = trim((string) $token); $c = self::settings(); if ($c['site_key'] === '' || $c['secret_key'] === '') { return self::record(array('ok' => false, 'stage' => 'keys', 'error' => 'missing-keys', 'message' => __('Both keys must be saved before they can be tested.', 'ai-eshop-optimizer'))); } if ($token === '') { return self::record(array('ok' => false, 'stage' => 'sitekey', 'error' => 'no-token', 'message' => sprintf( __('The browser could not produce a reCAPTCHA token. Usually this means the site key is not registered for %s in the Google reCAPTCHA console, or it is a v2 key rather than a v3 "score based" key.', 'ai-eshop-optimizer'), self::expected_host() ))); } $response = wp_remote_post(self::VERIFY_URL, array( 'timeout' => 8, 'redirection' => 0, 'body' => array('secret' => $c['secret_key'], 'response' => $token), )); if (is_wp_error($response)) { return array('ok' => false, 'stage' => 'unreachable', 'error' => 'transport', 'message' => __('Could not reach Google to check the keys. Try again in a moment; nothing has been changed.', 'ai-eshop-optimizer')); } $decoded = json_decode((string) wp_remote_retrieve_body($response), true); if (!is_array($decoded)) { return array('ok' => false, 'stage' => 'unreachable', 'error' => 'unparseable', 'message' => __('Google returned an unreadable response. Nothing has been changed.', 'ai-eshop-optimizer')); } $codes = isset($decoded['error-codes']) && is_array($decoded['error-codes']) ? array_map('strval', $decoded['error-codes']) : array(); if (empty($decoded['success'])) { if (array_intersect($codes, array('invalid-input-secret', 'missing-input-secret'))) { return self::record(array('ok' => false, 'stage' => 'secret', 'error' => implode(',', $codes), 'message' => __('The browser produced a token, so the site key is fine, but Google rejected the secret key. The two keys are probably from different reCAPTCHA registrations.', 'ai-eshop-optimizer'))); } if (in_array('timeout-or-duplicate', $codes, true)) { return array('ok' => false, 'stage' => 'retry', 'error' => implode(',', $codes), 'message' => __('That test token had already expired. Press Test again.', 'ai-eshop-optimizer')); } return self::record(array('ok' => false, 'stage' => 'pair', 'error' => implode(',', $codes), 'message' => sprintf( __('Google rejected the verification (%s). Check that both keys come from the same v3 registration.', 'ai-eshop-optimizer'), implode(', ', $codes) !== '' ? implode(', ', $codes) : 'unknown' ))); } $hostname = isset($decoded['hostname']) ? strtolower((string) $decoded['hostname']) : ''; $expected = self::expected_host(); $host_ok = ($hostname === '' || $expected === '' || $hostname === $expected || 'www.' . $hostname === $expected || $hostname === 'www.' . $expected); return self::record(array( 'ok' => true, 'stage' => 'full', 'error' => $host_ok ? '' : 'hostname-mismatch', 'hostname' => $hostname, 'score' => isset($decoded['score']) ? (float) $decoded['score'] : null, 'message' => $host_ok ? sprintf( __('Keys verified on %s. Protection can be switched on safely.', 'ai-eshop-optimizer'), $hostname !== '' ? $hostname : $expected ) : sprintf( __('Keys work, but Google recorded the check against %1$s rather than %2$s. The key is shared with another site — that still functions, but scores and reports will be mixed together.', 'ai-eshop-optimizer'), $hostname, $expected ), )); } }