PluginProbe
WP-Members Membership Plugin / trunk
WP-Members Membership Plugin vtrunk
trunk 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.4.2 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.4.9.1 3.4.9.2 3.4.9.3 3.4.9.4 3.4.9.5 3.4.9.6 3.4.9.7 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 All 34 releases
wp-members / includes / class-wp-members-captcha.php

class-wp-members-captcha.php in WP-Members Membership Plugin trunk, at includes/class-wp-members-captcha.php

431 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The WP_Members_Captcha Class.
4 *
5 * This is the WP_Members Captcha object class. This class contains functions
6 * for handling the various captchas that the plugin natively supports. This
7 * includes reCAPTCHA v2/v3, Really Simple CAPTCHA, and hCaptcha.
8 *
9 * @package WP-Members
10 * @subpackage WP_Members_Captcha Object Class
11 * @since 3.0.0
12 */
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit();
17 }
18
19 class WP_Members_Captcha {
20
21 /**
22 * Gets which CAPTCHA is set.
23 *
24 * @since 3.3.5
25 */
26 static function type( $decode = false ) {
27 global $wpmem;
28 $value = ( false !== $decode ) ? $decode : $wpmem->captcha;
29 switch ( $value ) {
30 case 0:
31 return "Disabled";
32 break;
33 case 1:
34 case 3:
35 return "recaptcha_v2";
36 break;
37 case 4:
38 return "recaptcha_v3";
39 break;
40 case 5:
41 return "hcaptcha";
42 break;
43 case 2:
44 default:
45 return "rs_captcha";
46 break;
47 }
48 }
49
50 /**
51 * Display a CAPTCHA.
52 *
53 * @since 3.3.4
54 * @since 3.3.6 $type defaults to false, so captcha defaults to $wpmem setting.
55 *
56 * @param string $type Type of captcha to display.
57 * @param array $keys Google reCAPTCHA keys (if used).
58 */
59 static function show( $type = false, $key = false ) {
60 if ( false === $type ) {
61 $type = self::type();
62 }
63 if ( 'rs_captcha' == $type ) {
64 return self::rs_captcha();
65 } elseif ( 'hcaptcha' == $type ) {
66 return self::hcaptcha( $key );
67 } else {
68 return self::recaptcha( $key );
69 }
70 }
71
72 /**
73 * Create a hCaptcha form.
74 *
75 * @since 3.3.5
76 *
77 * @param string $key Your hCaptcha API key.
78 * @return string $html The form HTML.
79 */
80 static function hcaptcha( $key = false ) {
81
82 if ( false === $key ) {
83 $opts = get_option( 'wpmembers_captcha' );
84 $key = $opts['hcaptcha']['api_key'];
85 }
86 $html = '<div class="h-captcha" data-sitekey="' . $key . '"></div>';
87 $html .= '<script src="https://hcaptcha.com/1/api.js" async defer></script>';
88 /** This filter is defined in /includes/class-wp-members-captcha.php */
89 return apply_filters( 'wpmem_captcha', $html );
90 }
91
92 /**
93 * Create reCAPTCHA form.
94 *
95 * @since 3.3.0 Replaces wpmem_inc_recaptcha().
96 * @since 3.3.5 Accepts API public key for static use.
97 *
98 * @global stdCalss $wpmem
99 * @param string $key Your reCAPTCHA public key.
100 * @return string $html HTML for reCAPTCHA display.
101 */
102 static function recaptcha( $key = false ) {
103
104 global $wpmem;
105
106 if ( false == $key ) {
107 $opts = get_option( 'wpmembers_captcha' );
108 $key = $opts['recaptcha']['public'];
109 }
110
111 /**
112 * Filters the URL used for google recaptcha API.
113 *
114 * @since 3.4.2
115 *
116 * @param string $url
117 */
118 $wpmem_recaptcha_url = apply_filters( 'wpmem_recaptcha_url', 'https://www.google.com/recaptcha/api.js' );
119
120 /*
121 * NOTE: DO NOT EDIT THIS. Use the filter hook found below.
122 *
123 * Don't know how to use filter or action hooks to customize WP?
124 * See https://wpbitz.com/how-to-use-hooks-in-wordpress/
125 */
126 if ( 3 == $wpmem->captcha ) {
127 $html = '<script src="' . $wpmem_recaptcha_url . '" async defer></script>
128 <div class="g-recaptcha" data-sitekey="' . $key . '"></div>';
129 } else {
130 $html = '<script src="' . $wpmem_recaptcha_url . '?render=' . $key . '"></script>';
131 $html.= "<script>
132 grecaptcha.ready(function () {
133 grecaptcha.execute('" . $key . "', { action: 'contact' }).then(function (token) {
134 var recaptchaResponse = document.getElementById('recaptchaResponse');
135 recaptchaResponse.value = token;
136 });
137 });
138 </script>";
139 $html.= '<input type="hidden" name="recaptcha_response" id="recaptchaResponse">';
140 }
141
142 /**
143 * Filter the reCAPTCHA HTML.
144 *
145 * @since 2.7.4
146 * @deprecated 3.3.5 Use wpmem_captcha instead.
147 *
148 * @param string $html A string of HTML for the reCAPTCHA.
149 */
150 $html = apply_filters_deprecated( 'wpmem_recaptcha', array( $html ), '3.3.5', 'wpmem_captcha' );
151
152
153 /**
154 * Filter the captcha HTML.
155 *
156 * @since 3.3.5
157 *
158 * @param string $html A string of HTML for the registration captcha.
159 */
160 return apply_filters( 'wpmem_captcha', $html );
161 }
162
163 /**
164 * Create Really Simple CAPTCHA.
165 *
166 * @since 3.3.0 Replaces wpmem_build_rs_captcha().
167 *
168 * @return string|array {
169 * HTML string, OR array of form elements for Really Simple CAPTCHA.
170 *
171 * @type string label_text The raw text used for the label.
172 * @type string label The HTML for the label.
173 * @type string field The input tag and the CAPTCHA image.
174 * }
175 */
176 static function rs_captcha( $return = 'string' ) {
177
178 if ( defined( 'REALLYSIMPLECAPTCHA_VERSION' ) ) {
179 // setup defaults
180 $defaults = array(
181 'characters' => 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789',
182 'num_char' => '4',
183 'dim_w' => '72',
184 'dim_h' => '30',
185 'font_color' => '0,0,0',
186 'bg_color' => '255,255,255',
187 'font_size' => '12',
188 'kerning' => '14',
189 'img_type' => 'png',
190 );
191 $opts = get_option( 'wpmembers_captcha' );
192
193 $args = ( isset( $opts['really_simple'] ) && is_array( $opts['really_simple'] ) ) ? $opts['really_simple'] : array();
194 $args = wp_parse_args( $args, $defaults );
195
196 $rs_captcha = new ReallySimpleCaptcha();
197 $rs_captcha->chars = $args['characters'];
198 $rs_captcha->char_length = $args['num_char'];
199 $rs_captcha->img_size = array( $args['dim_w'], $args['dim_h'] );
200 $rs_captcha->fg = explode( ",", $args['font_color'] );
201 $rs_captcha->bg = explode( ",", $args['bg_color'] );
202 $rs_captcha->font_size = $args['font_size'];
203 $rs_captcha->font_char_width = $args['kerning'];
204 $rs_captcha->img_type = $args['img_type'];
205
206 $rs_captcha_word = $rs_captcha->generate_random_word();
207 $rs_captcha_prefix = mt_rand();
208 $rs_captcha_image_name = $rs_captcha->generate_image( $rs_captcha_prefix, $rs_captcha_word );
209
210 /**
211 * Filters the default Really Simple Captcha folder location.
212 *
213 * @since 3.0
214 *
215 * @param string The default location of RS Captcha.
216 */
217 $rs_captcha_image_url = apply_filters( 'wpmem_rs_captcha_folder', get_bloginfo( 'wpurl' ) . '/wp-content/plugins/really-simple-captcha/tmp/' );
218
219 $img_w = $rs_captcha->img_size[0];
220 $img_h = $rs_captcha->img_size[1];
221 $src = $rs_captcha_image_url . $rs_captcha_image_name;
222 $size = $rs_captcha->char_length;
223 $pre = $rs_captcha_prefix;
224
225 /*
226 * NOTE: DO NOT EDIT THIS. Use the filter hook found below.
227 *
228 * Don't know how to use filter or action hooks to customize WP?
229 * See https://wpbitz.com/how-to-use-hooks-in-wordpress/
230 */
231 $captcha_rows_args = array(
232 'label_text' => wpmem_get_text( 'register_rscaptcha' ),
233 'code_size' => esc_attr( $size ),
234 'prefix' => $pre,
235 'img_src' => esc_url( $src ),
236 'img_w' => esc_attr( $img_w ),
237 'img_h' => esc_attr( $img_h ),
238 'label' => '<label class="text" for="captcha">' . wpmem_get_text( 'register_rscaptcha' ) . '</label>',
239 'field' => '<input id="captcha_code" name="captcha_code" size="' . esc_attr( $size ) . '" type="text" class="textbox" required />',
240 'hidden' => '<input id="captcha_prefix" name="captcha_prefix" type="hidden" value="' . esc_attr( $pre ) . '" />',
241 'img' => '<img src="' . esc_url( $src ) . '" alt="captcha" width="' . esc_attr( $img_w ) . '" height="' . esc_attr( $img_h ) . '" />',
242 );
243
244 /**
245 * Filter the RS CAPTCHA HTML.
246 *
247 * @since 3.3.5
248 *
249 * @param array
250 */
251 $rows = apply_filters( 'wpmem_rs_captcha_rows', $captcha_rows_args );
252
253 if ( 'array' == $return ) {
254 return $rows;
255 } else {
256 $html = $rows['label'] . $rows['img'] . $rows['hidden'] . $rows['field'];
257 /** This filter is defined in /includes/class-wp-members-captcha.php */
258 return apply_filters( 'wpmem_captcha', $html );
259 }
260 } else {
261 return ( 'array' == $return ) ? array( 'field' => "Really Simple CAPTCHA is not enabled", 'label' => '', 'label_text' => '', 'img' => '', 'hidden' => '' ) : "Really Simple CAPTCHA is not enabled";
262 }
263 }
264
265 /**
266 * Process a captcha.
267 *
268 * @since 3.1.6
269 * @since 3.3.0 Ported from wpmem_register_handle_captcha() in register.php.
270 * @since 3.3.4 Added argument to specify which captcha type to validate.
271 * @since 3.4.8 Additional error checking on RS Captchas
272 *
273 * @global $wpmem_themsg
274 * @param $which_captcha
275 * @return $string
276 */
277 static function validate( $which_captcha = false, $secret = false ) {
278
279 global $wpmem_themsg;
280
281 $captcha = ( ! $which_captcha ) ? self::type() : $which_captcha;
282
283 if ( 'rs_captcha' == $captcha ) {
284 if ( defined( 'REALLYSIMPLECAPTCHA_VERSION' ) ) {
285 // Validate Really Simple Captcha.
286 $rs_captcha = new ReallySimpleCaptcha();
287 // This variable holds the CAPTCHA image prefix, which corresponds to the correct answer.
288 $rs_captcha_prefix = ( wpmem_get( 'captcha_prefix', false ) );
289 if ( ! $rs_captcha_prefix ) {
290 $wpmem_themsg = wpmem_get_text( 'rs_captcha_error' );
291 return false;
292 }
293 // This variable holds the CAPTCHA response, entered by the user.
294 $rs_captcha_code = ( wpmem_get('captcha_code', false ) );
295 if ( ! $rs_captcha_code ) {
296 $wpmem_themsg = wpmem_get_text( 'rs_captcha_empty' );
297 return false;
298 }
299 // Check CAPTCHA validity.
300 $rs_captcha_correct = ( $rs_captcha->check( $rs_captcha_prefix, $rs_captcha_code ) ) ? true : false;
301 // Clean up the tmp directory.
302 $rs_captcha->remove( $rs_captcha_prefix );
303 $rs_captcha->cleanup();
304 // If CAPTCHA validation fails (incorrect value entered in CAPTCHA field), return an error.
305 if ( ! $rs_captcha_correct ) {
306 $wpmem_themsg = wpmem_get_text( 'rs_captcha_wrong' );
307 return false;
308 }
309 }
310
311 } elseif ( 'hcaptcha' == $captcha ) {
312
313 // Get the captcha settings (api keys).
314 if ( ! $secret ) {
315 $opts = get_option( 'wpmembers_captcha' );
316 $secret = $opts['hcaptcha']['secret'];
317 }
318
319 $captcha = wpmem_get( 'h-captcha-response', false );
320
321 // If there is no captcha value, return error.
322 if ( false === $captcha ) {
323 $wpmem_themsg = wpmem_get_text( 'reg_empty_captcha' );
324 return false;
325 }
326
327 // Validate the captcha.
328 $response = wp_remote_post( 'https://hcaptcha.com/siteverify', array(
329 'body' => array(
330 'secret' => $secret,
331 'response' => $captcha,
332 ) ) );
333
334 // Decode the json response.
335 $response = json_decode( wp_remote_retrieve_body( $response, true ) );
336
337 if ( $response->success ) {
338 // your success code goes here
339 } else {
340 $wpmem_themsg = wpmem_get_text( 'reg_invalid_captcha' );
341 return false;
342 }
343
344 } else {
345
346 // It is reCAPTCHA.
347 $recaptcha_verify_url = 'https://www.google.com/recaptcha/api/siteverify?';
348
349 // Get the captcha settings (api keys).
350 if ( ! $secret ) {
351 $opts = get_option( 'wpmembers_captcha' );
352 $secret = $opts['recaptcha']['private'];
353 }
354
355 if ( 'recaptcha_v2' == $captcha ) {
356
357 $captcha = wpmem_get( 'g-recaptcha-response', false );
358
359 // If there is no captcha value, return error.
360 if ( false === $captcha ) {
361 $wpmem_themsg = wpmem_get_text( 'reg_empty_captcha' );
362 return false;
363 }
364
365 // Build URL for captcha evaluation.
366 $url = $recaptcha_verify_url . http_build_query([
367 'secret' => $secret,
368 'response' => $captcha,
369 'remoteip' => wpmem_get_user_ip(),
370 ]);
371
372 // Validate the captcha.
373 $response = wp_remote_fopen( $url );
374
375 // Decode the json response.
376 $response = json_decode( $response, true );
377
378 // If captcha validation was unsuccessful.
379 if ( false == $response['success'] ) {
380 $wpmem_themsg = wpmem_get_text( 'reg_invalid_captcha' );
381 if ( WP_DEBUG && isset( $response['error-codes'] ) ) {
382 $wpmem_themsg.= '<br /><br />';
383 foreach( $response['error-codes'] as $code ) {
384 $wpmem_themsg.= "Error code: " . $code . "<br />";
385 }
386 }
387 return false;
388 }
389 } elseif ( 'recaptcha_v3' == $captcha ) {
390 $captcha = wpmem_get( 'recaptcha_response', false );
391
392 if ( false === $captcha ) {
393 $wpmem_themsg = wpmem_get_text( 'reg_empty_captcha' );
394 return false;
395 }
396
397 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && false !== $captcha ) {
398
399 // Make and decode POST request:
400 $url = $recaptcha_verify_url . http_build_query([
401 'secret' => $secret,
402 'response' => $captcha,
403 ]);
404 $recaptcha = file_get_contents( $url );
405 $recaptcha = json_decode( $recaptcha );
406
407 /**
408 * Filters the reCAPTCHA v3 score.
409 * @link https://developers.google.com/recaptcha/docs/v3
410 *
411 * @since 3.3.9
412 *
413 * @param int $score
414 */
415 $score = apply_filters( 'wpmem_recaptcha_score', 0.5 );
416 // Take action based on the score returned:
417 if ( $recaptcha->score >= $score ) {
418 return true;
419 } else {
420 $wpmem_themsg = wpmem_get_text( 'reg_invalid_captcha' );
421 return false;
422 }
423 } else {
424 return false;
425 }
426 }
427 }
428
429 return true;
430 }
431 }