| @@ -1135,8 +1135,52 @@ | ||
| 1135 | 1135 | return ''; |
| 1136 | 1136 | } |
| 1137 | 1137 | |
| 1138 | 1138 | |
| 1139 | +/** | |
| 1140 | + * Returns the AJAX actions that check the login/checkout credentials before the real form submission is sent. | |
| 1141 | + * | |
| 1142 | + * Those requests run the whole authentication stack, so our CAPTCHA check runs too and the token gets spent | |
| 1143 | + * with the CAPTCHA provider, but they never log the user in - the browser still submits the form afterwards | |
| 1144 | + * with the very same token. CAPTCHA tokens are single use, so verifying one a second time comes back as a | |
| 1145 | + * duplicate and the login fails. For these actions we remember the successful verification and reuse it once, | |
| 1146 | + * when the actual form submission arrives. | |
| 1147 | + */ | |
| 1148 | +function wppb_get_captcha_prevalidation_actions() { | |
| 1149 | + return apply_filters( 'wppb_captcha_prevalidation_actions', array( | |
| 1150 | + 'pms_validate_checkout', // Paid Member Subscriptions checkout validation | |
| 1151 | + 'wordfence_ls_authenticate', // Wordfence Login Security login pre-flight, used to decide if it needs to ask for a 2FA code | |
| 1152 | + ) ); | |
| 1153 | +} | |
| 1154 | + | |
| 1155 | +/* Whether the current request is one of the CAPTCHA pre-validation AJAX calls above */ | |
| 1156 | +function wppb_is_captcha_prevalidation_request() { | |
| 1157 | + if ( ! wp_doing_ajax() || empty( $_POST['action'] ) || ! is_string( $_POST['action'] ) ) /* phpcs:ignore WordPress.Security.NonceVerification.Missing */ | |
| 1158 | + return false; | |
| 1159 | + | |
| 1160 | + return in_array( sanitize_text_field( $_POST['action'] ), wppb_get_captcha_prevalidation_actions(), true ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing */ | |
| 1161 | +} | |
| 1162 | + | |
| 1163 | +/** | |
| 1164 | + * Drops pre-validated CAPTCHA tokens that were never claimed by a form submission. | |
| 1165 | + * | |
| 1166 | + * A pre-validation that is not followed by a submission (wrong password, abandoned login, bots) leaves its | |
| 1167 | + * entry behind, so without this the option would keep growing on sites where every login is pre-validated. | |
| 1168 | + */ | |
| 1169 | +function wppb_prune_captcha_prevalidations( $saved ) { | |
| 1170 | + if ( ! is_array( $saved ) ) | |
| 1171 | + return array(); | |
| 1172 | + | |
| 1173 | + $lifetime = apply_filters( 'wppb_captcha_prevalidation_lifetime', 15 * MINUTE_IN_SECONDS ); | |
| 1174 | + | |
| 1175 | + foreach ( $saved as $token => $validated_at ) { | |
| 1176 | + if ( ! is_int( $validated_at ) || ( time() - $validated_at ) > $lifetime ) | |
| 1177 | + unset( $saved[ $token ] ); | |
| 1178 | + } | |
| 1179 | + | |
| 1180 | + return $saved; | |
| 1181 | +} | |
| 1182 | + | |
| 1139 | 1183 | /* Function for displaying reCAPTCHA error on Login and Recover Password forms */ |
| 1140 | 1184 | function wppb_recaptcha_field_error($field_title='') { |
| 1141 | 1185 | |
| 1142 | 1186 | $recaptcha_field = wppb_get_recaptcha_field(); |