Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
- name: Run linting
run: composer run-script lint

- name: Run static analysis
run: composer run-script phpstan

- name: Run test suite
run: composer run-script test

Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"require-dev": {
"phpunit/phpunit": "^13",
"friendsofphp/php-cs-fixer": "^3.94",
"php-coveralls/php-coveralls": "^2.9"
"php-coveralls/php-coveralls": "^2.9",
"phpstan/phpstan": "^2.1"
},
"autoload": {
"psr-4": {
Expand All @@ -30,6 +31,7 @@
"scripts": {
"lint": "vendor/bin/php-cs-fixer -vvv check --using-cache=no",
"lint-fix": "vendor/bin/php-cs-fixer -vvv fix --using-cache=no",
"phpstan": "vendor/bin/phpstan",
"test": "XDEBUG_MODE=coverage vendor/bin/phpunit",
"serve-examples": "@php -S localhost:8080 -t examples"
},
Expand Down
55 changes: 54 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion examples/appengine-https.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
if ('http' === $_SERVER['HTTP_X_FORWARDED_PROTO']) {
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']);

/** @var string $serverName */
$serverName = $_SERVER['SERVER_NAME'];

/** @var string $requestUri */
$requestUri = $_SERVER['REQUEST_URI'];
header('Location: https://'.$serverName.$requestUri);

exit(0);
}
Expand Down
9 changes: 5 additions & 4 deletions examples/recaptcha-content-security-policy.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{v3: array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v3']['site'];
$secret = $config['v3']['secret'];
Expand Down Expand Up @@ -115,7 +116,7 @@
<p><strong>NOTE:</strong>This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in <a href="https://www.google.com/recaptcha/admin" target="_blank">your admin interface</a> and adjust your own threshold accordingly. <strong>Do not raise issues regarding the score you see here.</strong></p>
<ol id="recaptcha-steps">
<li class="step0">reCAPTCHA script loading</li>
<li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li>
<li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo (string) $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li>
<li class="step2 hidden">Received token from reCAPTCHA service, sending to our backend with:
<pre class="token">fetch('/recaptcha-v3-verify.php?token=abc123</pre></li>
<li class="step3 hidden">Received response from our backend: <pre class="response">{"json": "from-backend"}</pre></li>
Expand All @@ -128,7 +129,7 @@
const steps = document.getElementById('recaptcha-steps');
grecaptcha.ready(function() {
document.querySelector('.step1').classList.remove('hidden');
grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) {
grecaptcha.execute('<?php echo (string) $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) {
document.querySelector('.token').innerHTML = 'fetch(\'/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token=\'' + token;
document.querySelector('.step2').classList.remove('hidden');

Expand All @@ -143,7 +144,7 @@
};
</script>
<!-- Add the nonce value for the reCAPTCHA library to its script tag -->
<script async defer src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>&onload=onloadCallback" nonce="<?php echo $nonce; ?>"></script>
<script async defer src="https://www.google.com/recaptcha/api.js?render=<?php echo (string) $siteKey; ?>&onload=onloadCallback" nonce="<?php echo $nonce; ?>"></script>

<?php
}?>
Expand Down
19 changes: 15 additions & 4 deletions examples/recaptcha-v2-checkbox-explicit.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{'v2-standard': array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v2-standard']['site'];
$secret = $config['v2-standard']['secret'];
Expand Down Expand Up @@ -91,15 +92,25 @@
<?php
// If the form submission includes the "g-captcha-response" field
// Create an instance of the service using your secret
/** @var string $secret */
$recaptcha = new ReCaptcha($secret);

// If file_get_contents() is locked down on your PHP installation to disallow
// its use with URLs, then you can use the alternative request method instead.
// This makes use of fsockopen() instead.
// $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost());
// Make the call to verify the response and also pass the user's IP address
$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME'])
->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR'])
/** @var string $serverName */
$serverName = $_SERVER['SERVER_NAME'];

/** @var string $responseKey */
$responseKey = $_POST[ReCaptcha::RESPONSE_KEY];

/** @var null|string $remoteAddr */
$remoteAddr = $_SERVER['REMOTE_ADDR'];

$resp = $recaptcha->setExpectedHostname($serverName)
->verify($responseKey, $remoteAddr)
;

if ($resp->isSuccess()) {
Expand Down Expand Up @@ -139,7 +150,7 @@
var onloadCallback = function() {
var captchaContainer = document.querySelector('.g-recaptcha');
grecaptcha.render(captchaContainer, {
'sitekey' : '<?php echo $siteKey; ?>'
'sitekey' : '<?php echo (string) $siteKey; ?>'
});
document.querySelector('button[type="submit"]').disabled = false;
};
Expand Down
19 changes: 15 additions & 4 deletions examples/recaptcha-v2-checkbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{'v2-standard': array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v2-standard']['site'];
$secret = $config['v2-standard']['secret'];
Expand Down Expand Up @@ -91,6 +92,7 @@
<?php
// If the form submission includes the "g-captcha-response" field
// Create an instance of the service using your secret
/** @var string $secret */
$recaptcha = new ReCaptcha($secret);

// If file_get_contents() is locked down on your PHP installation to disallow
Expand All @@ -99,8 +101,17 @@
// $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost());

// Make the call to verify the response and also pass the user's IP address
$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME'])
->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR'])
/** @var string $serverName */
$serverName = $_SERVER['SERVER_NAME'];

/** @var string $responseKey */
$responseKey = $_POST[ReCaptcha::RESPONSE_KEY];

/** @var null|string $remoteAddr */
$remoteAddr = $_SERVER['REMOTE_ADDR'];

$resp = $recaptcha->setExpectedHostname($serverName)
->verify($responseKey, $remoteAddr)
;
if ($resp->isSuccess()) {
// If the response is a success, that's it!
Expand Down Expand Up @@ -130,7 +141,7 @@
<label class="form-field">Example input A: <input type="text" name="ex-a" value="foo"></label>
<label class="form-field">Example input B: <input type="text" name="ex-b" value="bar"></label>
<!-- Default behaviour looks for the g-recaptcha class with a data-sitekey attribute -->
<div class="g-recaptcha form-field" data-sitekey="<?php echo $siteKey; ?>"></div>
<div class="g-recaptcha form-field" data-sitekey="<?php echo (string) $siteKey; ?>"></div>
<!-- Submitting before the widget loads will result in a missing-input-response error so you need to verify server side -->
<button class="form-field" type="submit">Submit <span aria-hidden="true">↦</span></button>
</fieldset>
Expand Down
19 changes: 15 additions & 4 deletions examples/recaptcha-v2-invisible.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{'v2-invisible': array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v2-invisible']['site'];
$secret = $config['v2-invisible']['secret'];
Expand Down Expand Up @@ -91,6 +92,7 @@
<?php
// If the form submission includes the "g-captcha-response" field
// Create an instance of the service using your secret
/** @var string $secret */
$recaptcha = new ReCaptcha($secret);

// If file_get_contents() is locked down on your PHP installation to disallow
Expand All @@ -99,8 +101,17 @@
// $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost());

// Make the call to verify the response and also pass the user's IP address
$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME'])
->verify($_POST[ReCaptcha::RESPONSE_KEY], $_SERVER['REMOTE_ADDR'])
/** @var string $serverName */
$serverName = $_SERVER['SERVER_NAME'];

/** @var string $responseKey */
$responseKey = $_POST[ReCaptcha::RESPONSE_KEY];

/** @var null|string $remoteAddr */
$remoteAddr = $_SERVER['REMOTE_ADDR'];

$resp = $recaptcha->setExpectedHostname($serverName)
->verify($responseKey, $remoteAddr)
;
if ($resp->isSuccess()) {
// If the response is a success, that's it!
Expand Down Expand Up @@ -129,7 +140,7 @@
<legend>An example form</legend>
<label class="form-field">Example input A: <input type="text" name="ex-a" value="foo"></label>
<label class="form-field">Example input B: <input type="text" name="ex-b" value="bar"></label>
<button class="g-recaptcha form-field" data-sitekey="<?php echo $siteKey; ?>" data-callback='onSubmit'>Submit <span aria-hidden="true">↦</span></button>
<button class="g-recaptcha form-field" data-sitekey="<?php echo (string) $siteKey; ?>" data-callback='onSubmit'>Submit <span aria-hidden="true">↦</span></button>
</fieldset>
</form>
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>" async defer></script>
Expand Down
9 changes: 5 additions & 4 deletions examples/recaptcha-v3-request-scores.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{v3: array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v3']['site'];
$secret = $config['v3']['secret'];
Expand Down Expand Up @@ -90,18 +91,18 @@
<p><strong>NOTE:</strong>This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in <a href="https://www.google.com/recaptcha/admin" target="_blank">your admin interface</a> and adjust your own threshold accordingly. <strong>Do not raise issues regarding the score you see here.</strong></p>
<ol id="recaptcha-steps">
<li class="step0">reCAPTCHA script loading</li>
<li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li>
<li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo (string) $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li>
<li class="step2 hidden">Received token from reCAPTCHA service, sending to our backend with:
<pre class="token">fetch('/recaptcha-v3-verify.php?token=abc123</pre></li>
<li class="step3 hidden">Received response from our backend: <pre class="response">{"json": "from-backend"}</pre></li>
</ol>
<p><a href="/recaptcha-v3-request-scores.php"><span aria-hidden="true">⤴️</span> Try again</a></p>
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>"></script>
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo (string) $siteKey; ?>"></script>
<script>
const steps = document.getElementById('recaptcha-steps');
grecaptcha.ready(function() {
document.querySelector('.step1').classList.remove('hidden');
grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) {
grecaptcha.execute('<?php echo (string) $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) {
document.querySelector('.token').innerHTML = 'fetch(\'/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token=\'' + token;
document.querySelector('.step2').classList.remove('hidden');

Expand Down
23 changes: 19 additions & 4 deletions examples/recaptcha-v3-verify.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,34 @@
$secret = '';

// Copy the config.php.dist file to config.php and update it with your keys to run the examples
if ('' == $siteKey && is_readable(__DIR__.'/config.php')) {
if (is_readable(__DIR__.'/config.php')) {
/** @var array{v3: array{site: string, secret: string}} $config */
$config = include __DIR__.'/config.php';
$siteKey = $config['v3']['site'];
$secret = $config['v3']['secret'];
}

// Effectively we're providing an API endpoint here that will accept the token, verify it, and return the action / score to the page
// In production, always sanitize and validate the input you retrieve from the request.
/** @var string $secret */
$recaptcha = new ReCaptcha($secret);
$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME'])
->setExpectedAction($_GET['action'])

/** @var string $serverName */
$serverName = $_SERVER['SERVER_NAME'];

/** @var string $action */
$action = $_GET['action'];

/** @var string $token */
$token = $_GET['token'];

/** @var null|string $remoteAddr */
$remoteAddr = $_SERVER['REMOTE_ADDR'];

$resp = $recaptcha->setExpectedHostname($serverName)
->setExpectedAction($action)
->setScoreThreshold(0.5)
->verify($_GET['token'], $_SERVER['REMOTE_ADDR'])
->verify($token, $remoteAddr)
;
header('Content-type:application/json');
echo json_encode($resp->toArray());
6 changes: 6 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
level: max
paths:
- src
- tests
- examples
Loading
Loading