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

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. public const VERSION = 'php_1.3.0';
  45. /**
  46. * URL for reCAPTCHA siteverify API
  47. * @const string
  48. */
  49. public const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
  50. /**
  51. * Invalid JSON received
  52. * @const string
  53. */
  54. public const E_INVALID_JSON = 'invalid-json';
  55. /**
  56. * Could not connect to service
  57. * @const string
  58. */
  59. public const E_CONNECTION_FAILED = 'connection-failed';
  60. /**
  61. * Did not receive a 200 from the service
  62. * @const string
  63. */
  64. public const E_BAD_RESPONSE = 'bad-response';
  65. /**
  66. * Not a success, but no error codes received!
  67. * @const string
  68. */
  69. public const E_UNKNOWN_ERROR = 'unknown-error';
  70. /**
  71. * ReCAPTCHA response not provided
  72. * @const string
  73. */
  74. public const E_MISSING_INPUT_RESPONSE = 'missing-input-response';
  75. /**
  76. * Expected hostname did not match
  77. * @const string
  78. */
  79. public const E_HOSTNAME_MISMATCH = 'hostname-mismatch';
  80. /**
  81. * Expected APK package name did not match
  82. * @const string
  83. */
  84. public const E_APK_PACKAGE_NAME_MISMATCH = 'apk_package_name-mismatch';
  85. /**
  86. * Expected action did not match
  87. * @const string
  88. */
  89. public const E_ACTION_MISMATCH = 'action-mismatch';
  90. /**
  91. * Score threshold not met
  92. * @const string
  93. */
  94. public const E_SCORE_THRESHOLD_NOT_MET = 'score-threshold-not-met';
  95. /**
  96. * Challenge timeout
  97. * @const string
  98. */
  99. public 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. private $hostname;
  111. private $apkPackageName;
  112. private $action;
  113. private $threshold;
  114. private $timeoutSeconds;
  115. /**
  116. * Create a configured instance to use the reCAPTCHA service.
  117. *
  118. * @param string $secret The shared key between your site and reCAPTCHA.
  119. * @param RequestMethod $requestMethod method used to send the request. Defaults to POST.
  120. * @throws \RuntimeException if $secret is invalid
  121. */
  122. public function __construct($secret, RequestMethod $requestMethod = null)
  123. {
  124. if (empty($secret)) {
  125. throw new \RuntimeException('No secret provided');
  126. }
  127. if (!is_string($secret)) {
  128. throw new \RuntimeException('The provided secret must be a string');
  129. }
  130. $this->secret = $secret;
  131. $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod;
  132. }
  133. /**
  134. * Calls the reCAPTCHA siteverify API to verify whether the user passes
  135. * CAPTCHA test and additionally runs any specified additional checks
  136. *
  137. * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site.
  138. * @param string $remoteIp The end user's IP address.
  139. * @return Response Response from the service.
  140. */
  141. public function verify($response, $remoteIp = null)
  142. {
  143. // Discard empty solution submissions
  144. if (empty($response)) {
  145. $recaptchaResponse = new Response(false, array(self::E_MISSING_INPUT_RESPONSE));
  146. return $recaptchaResponse;
  147. }
  148. $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
  149. $rawResponse = $this->requestMethod->submit($params);
  150. $initialResponse = Response::fromJson($rawResponse);
  151. $validationErrors = array();
  152. if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) {
  153. $validationErrors[] = self::E_HOSTNAME_MISMATCH;
  154. }
  155. if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) {
  156. $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH;
  157. }
  158. if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) {
  159. $validationErrors[] = self::E_ACTION_MISMATCH;
  160. }
  161. if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) {
  162. $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET;
  163. }
  164. if (isset($this->timeoutSeconds)) {
  165. $challengeTs = strtotime($initialResponse->getChallengeTs());
  166. if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) {
  167. $validationErrors[] = self::E_CHALLENGE_TIMEOUT;
  168. }
  169. }
  170. if (empty($validationErrors)) {
  171. return $initialResponse;
  172. }
  173. return new Response(
  174. false,
  175. array_merge($initialResponse->getErrorCodes(), $validationErrors),
  176. $initialResponse->getHostname(),
  177. $initialResponse->getChallengeTs(),
  178. $initialResponse->getApkPackageName(),
  179. $initialResponse->getScore(),
  180. $initialResponse->getAction()
  181. );
  182. }
  183. /**
  184. * Provide a hostname to match against in verify()
  185. * This should be without a protocol or trailing slash, e.g. www.google.com
  186. *
  187. * @param string $hostname Expected hostname
  188. * @return ReCaptcha Current instance for fluent interface
  189. */
  190. public function setExpectedHostname($hostname)
  191. {
  192. $this->hostname = $hostname;
  193. return $this;
  194. }
  195. /**
  196. * Provide an APK package name to match against in verify()
  197. *
  198. * @param string $apkPackageName Expected APK package name
  199. * @return ReCaptcha Current instance for fluent interface
  200. */
  201. public function setExpectedApkPackageName($apkPackageName)
  202. {
  203. $this->apkPackageName = $apkPackageName;
  204. return $this;
  205. }
  206. /**
  207. * Provide an action to match against in verify()
  208. * This should be set per page.
  209. *
  210. * @param string $action Expected action
  211. * @return ReCaptcha Current instance for fluent interface
  212. */
  213. public function setExpectedAction($action)
  214. {
  215. $this->action = $action;
  216. return $this;
  217. }
  218. /**
  219. * Provide a threshold to meet or exceed in verify()
  220. * Threshold should be a float between 0 and 1 which will be tested as response >= threshold.
  221. *
  222. * @param float $threshold Expected threshold
  223. * @return ReCaptcha Current instance for fluent interface
  224. */
  225. public function setScoreThreshold($threshold)
  226. {
  227. $this->threshold = floatval($threshold);
  228. return $this;
  229. }
  230. /**
  231. * Provide a timeout in seconds to test against the challenge timestamp in verify()
  232. *
  233. * @param int $timeoutSeconds Expected hostname
  234. * @return ReCaptcha Current instance for fluent interface
  235. */
  236. public function setChallengeTimeout($timeoutSeconds)
  237. {
  238. $this->timeoutSeconds = $timeoutSeconds;
  239. return $this;
  240. }
  241. }