PluginProbe
The Innovative Form Builder – IvyForms / 1.5.1
The Innovative Form Builder – IvyForms v1.5.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Services / Security / Recaptcha / RecaptchaCredentialsValidator.php

RecaptchaCredentialsValidator.php in The Innovative Form Builder – IvyForms 1.5.1, at backend/src/Services/Security/Recaptcha/RecaptchaCredentialsValidator.php

178 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See COPYING.md for license details.
6 */
7
8 namespace IvyForms\Services\Security\Recaptcha;
9
10 use IvyForms\Services\Translations\BackendStrings;
11
12 /**
13 * Handles reCAPTCHA credentials validation
14 *
15 * Extracted from RecaptchaService to reduce complexity
16 *
17 * @package IvyForms\Services\Security
18 */
19 class RecaptchaCredentialsValidator
20 {
21 /**
22 * Validate reCAPTCHA key format
23 *
24 * @param string $key
25 * @return bool
26 */
27 public static function validateKeyFormat(string $key): bool
28 {
29 if (empty($key)) {
30 return false;
31 }
32
33 // reCAPTCHA keys are typically 40 characters long
34 if (strlen($key) !== 40) {
35 return false;
36 }
37
38 // Basic format validation - should contain alphanumeric characters and some special chars
39 if (!preg_match('/^[A-Za-z0-9_-]+$/', $key)) {
40 return false;
41 }
42
43 // reCAPTCHA site and secret keys typically start with specific prefixes
44 $validPrefixes = ['6L', '6I']; // Common reCAPTCHA key prefixes
45
46 foreach ($validPrefixes as $prefix) {
47 if (strpos($key, $prefix) === 0) {
48 return true;
49 }
50 }
51
52 return false;
53 }
54
55 /**
56 * Validate both site key and secret key formats
57 *
58 * @param string $siteKey
59 * @param string $secretKey
60 * @return array{success: bool, message: string}
61 */
62 public static function validateKeyFormats(string $siteKey, string $secretKey): array
63 {
64 $strings = BackendStrings::getSecurityStrings();
65
66 if (!self::validateKeyFormat($siteKey)) {
67 return [
68 'success' => false,
69 'message' => $strings['recaptcha_invalid_site_key']
70 ];
71 }
72
73 if (!self::validateKeyFormat($secretKey)) {
74 return [
75 'success' => false,
76 'message' => $strings['recaptcha_invalid_key_format']
77 ];
78 }
79
80 return ['success' => true, 'message' => ''];
81 }
82
83 /**
84 * Process Google API verification result
85 *
86 * @param array<string, mixed>|false $verificationResult
87 * @return array{success: bool, message: string}
88 */
89 public static function processVerificationResult($verificationResult): array
90 {
91 $strings = BackendStrings::getSecurityStrings();
92
93 if ($verificationResult === false) {
94 return [
95 'success' => false,
96 'message' => $strings['recaptcha_validation_network_error']
97 ];
98 }
99
100 return self::checkErrorCodes($verificationResult, $strings);
101 }
102
103 /**
104 * Check for specific error codes in verification result
105 *
106 * @param array<string, mixed> $verificationResult
107 * @param array<string, string> $strings
108 * @return array{success: bool, message: string}
109 */
110 private static function checkErrorCodes(array $verificationResult, array $strings): array
111 {
112 if (!isset($verificationResult['error-codes'])) {
113 return self::handleSuccessCase($verificationResult, $strings);
114 }
115
116 $errorCodes = $verificationResult['error-codes'];
117
118 if (in_array('invalid-input-secret', $errorCodes)) {
119 return [
120 'success' => false,
121 'message' => $strings['recaptcha_invalid_secret_key']
122 ];
123 }
124
125 if (in_array('missing-input-secret', $errorCodes)) {
126 return [
127 'success' => false,
128 'message' => $strings['recaptcha_keys_required']
129 ];
130 }
131
132 // If we get 'invalid-input-response' or 'missing-input-response',
133 // it means the secret key is valid but our test token is invalid
134 if (self::hasValidSecretKeyErrors($errorCodes)) {
135 return [
136 'success' => true,
137 'message' => $strings['recaptcha_credentials_valid']
138 ];
139 }
140
141 return self::handleSuccessCase($verificationResult, $strings);
142 }
143
144 /**
145 * Check if error codes indicate valid secret key
146 *
147 * @param array<string> $errorCodes
148 * @return bool
149 */
150 private static function hasValidSecretKeyErrors(array $errorCodes): bool
151 {
152 return in_array('invalid-input-response', $errorCodes) ||
153 in_array('missing-input-response', $errorCodes);
154 }
155
156 /**
157 * Handle the success case of verification
158 *
159 * @param array<string, mixed> $verificationResult
160 * @param array<string, string> $strings
161 * @return array{success: bool, message: string}
162 */
163 private static function handleSuccessCase(array $verificationResult, array $strings): array
164 {
165 if (!$verificationResult['success']) {
166 return [
167 'success' => false,
168 'message' => $strings['recaptcha_unable_to_validate']
169 ];
170 }
171
172 return [
173 'success' => true,
174 'message' => $strings['recaptcha_credentials_valid']
175 ];
176 }
177 }
178