PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | modules/sharedaddy/recaptcha.php +234 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,234 @@
1 +<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 +/**
3 + * Google reCAPTCHA utilities, for use in the sharing feature.
4 + *
5 + * @package automattic/jetpack
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 + exit( 0 );
10 +}
11 +
12 +/**
13 + * Class that handles reCAPTCHA.
14 + *
15 + * @deprecated 11.0
16 + */
17 +class Jetpack_ReCaptcha {
18 +
19 + /**
20 + * URL to which requests are POSTed.
21 + *
22 + * @const string
23 + */
24 + const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
25 +
26 + /**
27 + * Site key to use in HTML code.
28 + *
29 + * @var string
30 + */
31 + private $site_key;
32 +
33 + /**
34 + * Shared secret for the site.
35 + *
36 + * @var string
37 + */
38 + private $secret_key;
39 +
40 + /**
41 + * Config for reCAPTCHA instance.
42 + *
43 + * @var array
44 + */
45 + private $config;
46 +
47 + /**
48 + * Error codes returned from reCAPTCHA API.
49 + *
50 + * @see https://developers.google.com/recaptcha/docs/verify
51 + *
52 + * @var array
53 + */
54 + private $error_codes;
55 +
56 + /**
57 + * Create a configured instance to use the reCAPTCHA service.
58 + *
59 + * @param string $site_key Site key to use in HTML code.
60 + * @param string $secret_key Shared secret between site and reCAPTCHA server.
61 + * @param array $config Config array to optionally configure reCAPTCHA instance.
62 + */
63 + public function __construct( $site_key, $secret_key, $config = array() ) {
64 + $this->site_key = $site_key;
65 + $this->secret_key = $secret_key;
66 + $this->config = wp_parse_args( $config, $this->get_default_config() );
67 +
68 + $this->error_codes = array(
69 + 'missing-input-secret' => __( 'The secret parameter is missing', 'jetpack' ),
70 + 'invalid-input-secret' => __( 'The secret parameter is invalid or malformed', 'jetpack' ),
71 + 'missing-input-response' => __( 'The response parameter is missing', 'jetpack' ),
72 + 'invalid-input-response' => __( 'The response parameter is invalid or malformed', 'jetpack' ),
73 + 'invalid-json' => __( 'Invalid JSON', 'jetpack' ),
74 + 'unexpected-response' => __( 'Unexpected response', 'jetpack' ),
75 + 'unexpected-hostname' => __( 'Unexpected hostname', 'jetpack' ),
76 + );
77 + }
78 +
79 + /**
80 + * Get default config for this reCAPTCHA instance.
81 + *
82 + * @return array Default config
83 + */
84 + public function get_default_config() {
85 + return array(
86 + 'language' => get_locale(),
87 + 'script_async' => false,
88 + 'script_defer' => true,
89 + 'script_lazy' => false,
90 + 'tag_class' => 'g-recaptcha',
91 + 'tag_attributes' => array(
92 + 'theme' => 'light',
93 + 'type' => 'image',
94 + 'tabindex' => 0,
95 + ),
96 + );
97 + }
98 +
99 + /**
100 + * Calls the reCAPTCHA siteverify API to verify whether the user passes
101 + * CAPTCHA test.
102 + *
103 + * @param string $response The value of 'g-recaptcha-response' in the submitted
104 + * form.
105 + * @param string $remote_ip The end user's IP address.
106 + *
107 + * @return bool|WP_Error Returns true if verified. Otherwise WP_Error is returned.
108 + */
109 + public function verify( $response, $remote_ip ) {
110 + // No need make a request if response is empty.
111 + if ( empty( $response ) ) {
112 + return new WP_Error( 'missing-input-response', $this->error_codes['missing-input-response'], 400 );
113 + }
114 +
115 + $resp = wp_remote_post( self::VERIFY_URL, $this->get_verify_request_params( $response, $remote_ip ) );
116 + if ( is_wp_error( $resp ) ) {
117 + return $resp;
118 + }
119 +
120 + $resp_decoded = json_decode( wp_remote_retrieve_body( $resp ), true );
121 + if ( ! $resp_decoded ) {
122 + return new WP_Error( 'invalid-json', $this->error_codes['invalid-json'], 400 );
123 + }
124 +
125 + // Default error code and message.
126 + $error_code = 'unexpected-response';
127 + $error_message = $this->error_codes['unexpected-response'];
128 +
129 + // Use the first error code if exists.
130 + if ( isset( $resp_decoded['error-codes'] ) && is_array( $resp_decoded['error-codes'] ) ) {
131 + if ( isset( $resp_decoded['error-codes'][0] ) && isset( $this->error_codes[ $resp_decoded['error-codes'][0] ] ) ) {
132 + $error_message = $this->error_codes[ $resp_decoded['error-codes'][0] ];
133 + $error_code = $resp_decoded['error-codes'][0];
134 + }
135 + }
136 +
137 + if ( ! isset( $resp_decoded['success'] ) ) {
138 + return new WP_Error( $error_code, $error_message );
139 + }
140 +
141 + if ( true !== $resp_decoded['success'] ) {
142 + return new WP_Error( $error_code, $error_message );
143 + }
144 + // Validate the hostname matches expected source
145 + if ( isset( $resp_decoded['hostname'] ) ) {
146 + $url = wp_parse_url( get_home_url() );
147 +
148 + /**
149 + * Allow other valid hostnames.
150 + *
151 + * This can be useful in cases where the token hostname is expected to be
152 + * different from the get_home_url (ex. AMP recaptcha token contains a different hostname)
153 + *
154 + * @module sharedaddy
155 + *
156 + * @since 9.1.0
157 + *
158 + * @param array [ $url['host'] ] List of the valid hostnames to check against.
159 + */
160 + $valid_hostnames = apply_filters( 'jetpack_recaptcha_valid_hostnames', array( $url['host'] ) );
161 +
162 + if ( ! in_array( $resp_decoded['hostname'], $valid_hostnames, true ) ) {
163 + return new WP_Error( 'unexpected-host', $this->error_codes['unexpected-hostname'] );
164 + }
165 + }
166 +
167 + return true;
168 + }
169 +
170 + /**
171 + * Get siteverify request parameters.
172 + *
173 + * @param string $response The value of 'g-recaptcha-response' in the submitted
174 + * form.
175 + * @param string $remote_ip The end user's IP address.
176 + *
177 + * @return array
178 + */
179 + public function get_verify_request_params( $response, $remote_ip ) {
180 + return array(
181 + 'body' => array(
182 + 'secret' => $this->secret_key,
183 + 'response' => $response,
184 + 'remoteip' => $remote_ip,
185 + ),
186 + 'sslverify' => true,
187 + );
188 + }
189 +
190 + /**
191 + * Get reCAPTCHA HTML to render.
192 + *
193 + * @return string
194 + */
195 + public function get_recaptcha_html() {
196 + $url = sprintf(
197 + 'https://www.google.com/recaptcha/api.js?hl=%s',
198 + rawurlencode( $this->config['language'] )
199 + );
200 +
201 + $html = sprintf(
202 + '
203 + <div
204 + class="%s"
205 + data-sitekey="%s"
206 + data-theme="%s"
207 + data-type="%s"
208 + data-tabindex="%s"
209 + data-lazy="%s"
210 + data-url="%s"></div>
211 + ',
212 + esc_attr( $this->config['tag_class'] ),
213 + esc_attr( $this->site_key ),
214 + esc_attr( $this->config['tag_attributes']['theme'] ),
215 + esc_attr( $this->config['tag_attributes']['type'] ),
216 + esc_attr( $this->config['tag_attributes']['tabindex'] ),
217 + $this->config['script_lazy'] ? 'true' : 'false',
218 + esc_attr( $url )
219 + );
220 +
221 + if ( ! $this->config['script_lazy'] ) {
222 + $html .= sprintf(
223 + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
224 + '<script src="%s"%s%s></script>
225 + ',
226 + $url,
227 + $this->config['script_async'] && ! $this->config['script_defer'] ? ' async' : '',
228 + $this->config['script_defer'] ? ' defer' : ''
229 + );
230 + }
231 +
232 + return $html;
233 + }
234 +}