vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php line 254

Open in your IDE?
  1. <?php
  2. /**
  3. * This is a PHP library that handles calling reCAPTCHA.
  4. *
  5. * BSD 3-Clause License
  6. * @copyright (c) 2019, Google Inc.
  7. * @link https://www.google.com/recaptcha
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions are met:
  12. * 1. Redistributions of source code must retain the above copyright notice, this
  13. * list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. Neither the name of the copyright holder nor the names of its
  20. * contributors may be used to endorse or promote products derived from
  21. * this software without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  27. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  29. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  31. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. */
  34. namespace ReCaptcha;
  35. /**
  36. * reCAPTCHA client.
  37. */
  38. class ReCaptcha
  39. {
  40. /**
  41. * Version of this client library.
  42. * @const string
  43. */
  44. const VERSION = 'php_1.2.4';
  45. /**
  46. * URL for reCAPTCHA siteverify API
  47. * @const string
  48. */
  49. const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
  50. /**
  51. * Invalid JSON received
  52. * @const string
  53. */
  54. const E_INVALID_JSON = 'invalid-json';
  55. /**
  56. * Could not connect to service
  57. * @const string
  58. */
  59. const E_CONNECTION_FAILED = 'connection-failed';
  60. /**
  61. * Did not receive a 200 from the service
  62. * @const string
  63. */
  64. const E_BAD_RESPONSE = 'bad-response';
  65. /**
  66. * Not a success, but no error codes received!
  67. * @const string
  68. */
  69. const E_UNKNOWN_ERROR = 'unknown-error';
  70. /**
  71. * ReCAPTCHA response not provided
  72. * @const string
  73. */
  74. const E_MISSING_INPUT_RESPONSE = 'missing-input-response';
  75. /**
  76. * Expected hostname did not match
  77. * @const string
  78. */
  79. const E_HOSTNAME_MISMATCH = 'hostname-mismatch';
  80. /**
  81. * Expected APK package name did not match
  82. * @const string
  83. */
  84. const E_APK_PACKAGE_NAME_MISMATCH = 'apk_package_name-mismatch';
  85. /**
  86. * Expected action did not match
  87. * @const string
  88. */
  89. const E_ACTION_MISMATCH = 'action-mismatch';
  90. /**
  91. * Score threshold not met
  92. * @const string
  93. */
  94. const E_SCORE_THRESHOLD_NOT_MET = 'score-threshold-not-met';
  95. /**
  96. * Challenge timeout
  97. * @const string
  98. */
  99. const E_CHALLENGE_TIMEOUT = 'challenge-timeout';
  100. /**
  101. * Shared secret for the site.
  102. * @var string
  103. */
  104. private $secret;
  105. /**
  106. * Method used to communicate with service. Defaults to POST request.
  107. * @var RequestMethod
  108. */
  109. private $requestMethod;
  110. /**
  111. * Create a configured instance to use the reCAPTCHA service.
  112. *
  113. * @param string $secret The shared key between your site and reCAPTCHA.
  114. * @param RequestMethod $requestMethod method used to send the request. Defaults to POST.
  115. * @throws \RuntimeException if $secret is invalid
  116. */
  117. public function __construct($secret, RequestMethod $requestMethod = null)
  118. {
  119. if (empty($secret)) {
  120. throw new \RuntimeException('No secret provided');
  121. }
  122. if (!is_string($secret)) {
  123. throw new \RuntimeException('The provided secret must be a string');
  124. }
  125. $this->secret = $secret;
  126. $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod;
  127. }
  128. /**
  129. * Calls the reCAPTCHA siteverify API to verify whether the user passes
  130. * CAPTCHA test and additionally runs any specified additional checks
  131. *
  132. * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site.
  133. * @param string $remoteIp The end user's IP address.
  134. * @return Response Response from the service.
  135. */
  136. public function verify($response, $remoteIp = null)
  137. {
  138. // Discard empty solution submissions
  139. if (empty($response)) {
  140. $recaptchaResponse = new Response(false, array(self::E_MISSING_INPUT_RESPONSE));
  141. return $recaptchaResponse;
  142. }
  143. $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
  144. $rawResponse = $this->requestMethod->submit($params);
  145. $initialResponse = Response::fromJson($rawResponse);
  146. $validationErrors = array();
  147. if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) {
  148. $validationErrors[] = self::E_HOSTNAME_MISMATCH;
  149. }
  150. if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) {
  151. $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH;
  152. }
  153. if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) {
  154. $validationErrors[] = self::E_ACTION_MISMATCH;
  155. }
  156. if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) {
  157. $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET;
  158. }
  159. if (isset($this->timeoutSeconds)) {
  160. $challengeTs = strtotime($initialResponse->getChallengeTs());
  161. if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) {
  162. $validationErrors[] = self::E_CHALLENGE_TIMEOUT;
  163. }
  164. }
  165. if (empty($validationErrors)) {
  166. return $initialResponse;
  167. }
  168. return new Response(
  169. false,
  170. array_merge($initialResponse->getErrorCodes(), $validationErrors),
  171. $initialResponse->getHostname(),
  172. $initialResponse->getChallengeTs(),
  173. $initialResponse->getApkPackageName(),
  174. $initialResponse->getScore(),
  175. $initialResponse->getAction()
  176. );
  177. }
  178. /**
  179. * Provide a hostname to match against in verify()
  180. * This should be without a protocol or trailing slash, e.g. www.google.com
  181. *
  182. * @param string $hostname Expected hostname
  183. * @return ReCaptcha Current instance for fluent interface
  184. */
  185. public function setExpectedHostname($hostname)
  186. {
  187. $this->hostname = $hostname;
  188. return $this;
  189. }
  190. /**
  191. * Provide an APK package name to match against in verify()
  192. *
  193. * @param string $apkPackageName Expected APK package name
  194. * @return ReCaptcha Current instance for fluent interface
  195. */
  196. public function setExpectedApkPackageName($apkPackageName)
  197. {
  198. $this->apkPackageName = $apkPackageName;
  199. return $this;
  200. }
  201. /**
  202. * Provide an action to match against in verify()
  203. * This should be set per page.
  204. *
  205. * @param string $action Expected action
  206. * @return ReCaptcha Current instance for fluent interface
  207. */
  208. public function setExpectedAction($action)
  209. {
  210. $this->action = $action;
  211. return $this;
  212. }
  213. /**
  214. * Provide a threshold to meet or exceed in verify()
  215. * Threshold should be a float between 0 and 1 which will be tested as response >= threshold.
  216. *
  217. * @param float $threshold Expected threshold
  218. * @return ReCaptcha Current instance for fluent interface
  219. */
  220. public function setScoreThreshold($threshold)
  221. {
  222. $this->threshold = floatval($threshold);
  223. return $this;
  224. }
  225. /**
  226. * Provide a timeout in seconds to test against the challenge timestamp in verify()
  227. *
  228. * @param int $timeoutSeconds Expected hostname
  229. * @return ReCaptcha Current instance for fluent interface
  230. */
  231. public function setChallengeTimeout($timeoutSeconds)
  232. {
  233. $this->timeoutSeconds = $timeoutSeconds;
  234. return $this;
  235. }
  236. }