PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.1.2
Jetpack – WP Security, Backup, Speed, & Growth v9.1.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 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / modules / sharedaddy / recaptcha.php

recaptcha.php in Jetpack – WP Security, Backup, Speed, & Growth 9.1.2, at modules/sharedaddy/recaptcha.php

203 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class that handles reCAPTCHA.
5 */
6 class Jetpack_ReCaptcha {
7
8 /**
9 * URL to which requests are POSTed.
10 *
11 * @const string
12 */
13 const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
14
15 /**
16 * Site key to use in HTML code.
17 *
18 * @var string
19 */
20 private $site_key;
21
22 /**
23 * Shared secret for the site.
24 *
25 * @var string
26 */
27 private $secret_key;
28
29 /**
30 * Config for reCAPTCHA instance.
31 *
32 * @var array
33 */
34 private $config;
35
36 /**
37 * Error codes returned from reCAPTCHA API.
38 *
39 * @see https://developers.google.com/recaptcha/docs/verify
40 *
41 * @var array
42 */
43 private $error_codes;
44
45 /**
46 * Create a configured instance to use the reCAPTCHA service.
47 *
48 * @param string $site_key Site key to use in HTML code.
49 * @param string $secret_key Shared secret between site and reCAPTCHA server.
50 * @param array $config Config array to optionally configure reCAPTCHA instance.
51 */
52 public function __construct( $site_key, $secret_key, $config = array() ) {
53 $this->site_key = $site_key;
54 $this->secret_key = $secret_key;
55 $this->config = wp_parse_args( $config, $this->get_default_config() );
56
57 $this->error_codes = array(
58 'missing-input-secret' => __( 'The secret parameter is missing', 'jetpack' ),
59 'invalid-input-secret' => __( 'The secret parameter is invalid or malformed', 'jetpack' ),
60 'missing-input-response' => __( 'The response parameter is missing', 'jetpack' ),
61 'invalid-input-response' => __( 'The response parameter is invalid or malformed', 'jetpack' ),
62 'invalid-json' => __( 'Invalid JSON', 'jetpack' ),
63 'unexpected-response' => __( 'Unexpected response', 'jetpack' ),
64 'unexpected-hostname' => __( 'Unexpected hostname', 'jetpack' ),
65 );
66 }
67
68 /**
69 * Get default config for this reCAPTCHA instance.
70 *
71 * @return array Default config
72 */
73 public function get_default_config() {
74 return array(
75 'language' => get_locale(),
76 'script_async' => true,
77 'tag_class' => 'g-recaptcha',
78 'tag_attributes' => array(
79 'theme' => 'light',
80 'type' => 'image',
81 'tabindex' => 0,
82 ),
83 );
84 }
85
86 /**
87 * Calls the reCAPTCHA siteverify API to verify whether the user passes
88 * CAPTCHA test.
89 *
90 * @param string $response The value of 'g-recaptcha-response' in the submitted
91 * form.
92 * @param string $remote_ip The end user's IP address.
93 *
94 * @return bool|WP_Error Returns true if verified. Otherwise WP_Error is returned.
95 */
96 public function verify( $response, $remote_ip ) {
97 // No need make a request if response is empty.
98 if ( empty( $response ) ) {
99 return new WP_Error( 'missing-input-response', $this->error_codes['missing-input-response'], 400 );
100 }
101
102 $resp = wp_remote_post( self::VERIFY_URL, $this->get_verify_request_params( $response, $remote_ip ) );
103 if ( is_wp_error( $resp ) ) {
104 return $resp;
105 }
106
107 $resp_decoded = json_decode( wp_remote_retrieve_body( $resp ), true );
108 if ( ! $resp_decoded ) {
109 return new WP_Error( 'invalid-json', $this->error_codes['invalid-json'], 400 );
110 }
111
112 // Default error code and message.
113 $error_code = 'unexpected-response';
114 $error_message = $this->error_codes['unexpected-response'];
115
116 // Use the first error code if exists.
117 if ( isset( $resp_decoded['error-codes'] ) && is_array( $resp_decoded['error-codes'] ) ) {
118 if ( isset( $resp_decoded['error-codes'][0] ) && isset( $this->error_codes[ $resp_decoded['error-codes'][0] ] ) ) {
119 $error_message = $this->error_codes[ $resp_decoded['error-codes'][0] ];
120 $error_code = $resp_decoded['error-codes'][0];
121 }
122 }
123
124 if ( ! isset( $resp_decoded['success'] ) ) {
125 return new WP_Error( $error_code, $error_message );
126 }
127
128 if ( true !== $resp_decoded['success'] ) {
129 return new WP_Error( $error_code, $error_message );
130 }
131 // Validate the hostname matches expected source
132 if ( isset( $resp_decoded['hostname'] ) ) {
133 $url = wp_parse_url( get_home_url() );
134
135 /**
136 * Allow other valid hostnames.
137 *
138 * This can be useful in cases where the token hostname is expected to be
139 * different from the get_home_url (ex. AMP recaptcha token contains a different hostname)
140 *
141 * @module sharedaddy
142 *
143 * @since 9.1.0
144 *
145 * @param array [ $url['host'] ] List of the valid hostnames to check against.
146 */
147 $valid_hostnames = apply_filters( 'jetpack_recaptcha_valid_hostnames', array( $url['host'] ) );
148
149 if ( ! in_array( $resp_decoded['hostname'], $valid_hostnames, true ) ) {
150 return new WP_Error( 'unexpected-host', $this->error_codes['unexpected-hostname'] );
151 }
152 }
153
154 return true;
155 }
156
157 /**
158 * Get siteverify request parameters.
159 *
160 * @param string $response The value of 'g-recaptcha-response' in the submitted
161 * form.
162 * @param string $remote_ip The end user's IP address.
163 *
164 * @return array
165 */
166 public function get_verify_request_params( $response, $remote_ip ) {
167 return array(
168 'body' => array(
169 'secret' => $this->secret_key,
170 'response' => $response,
171 'remoteip' => $remote_ip,
172 ),
173 'sslverify' => true,
174 );
175 }
176
177 /**
178 * Get reCAPTCHA HTML to render.
179 *
180 * @return string
181 */
182 public function get_recaptcha_html() {
183 return sprintf(
184 '
185 <div
186 class="%s"
187 data-sitekey="%s"
188 data-theme="%s"
189 data-type="%s"
190 data-tabindex="%s"></div>
191 <script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=%s"%s></script>
192 ',
193 esc_attr( $this->config['tag_class'] ),
194 esc_attr( $this->site_key ),
195 esc_attr( $this->config['tag_attributes']['theme'] ),
196 esc_attr( $this->config['tag_attributes']['type'] ),
197 esc_attr( $this->config['tag_attributes']['tabindex'] ),
198 rawurlencode( $this->config['language'] ),
199 $this->config['script_async'] ? ' async' : ''
200 );
201 }
202 }
203