Invisible reCaptcha PHP vs JS verification - php

I have a simple question.
I added a custom form with Google Invisible Captcha to avoid spam.
Currently I have a something like this (just works fine):
<script>
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
</script>
<form id='demo-form' action="" method="POST">
<button class="g-recaptcha" data-sitekey="your_site_key" data-callback='onSubmit'>Submit</button>
<br/>
</form>
But to make it more "secure" I can add extra layer of verification via $_POST response as described below.
if( isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response']) ) {
// verify response
$verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secret_key.'&response='.$_POST['g-recaptcha-response']);
$responseData = json_decode($verifyResponse);
// if all good => proceed registration
if ($responseData->success) {
Do you think it worth adding this extra verification via PHP or it's fine to leave it as is ?

Related

Recaptcha V3 not always returning Score and Result

I tried implementing Recaptcha V3 on one of our websites forms recently, but ran in to an error where it wouldn't let some users submit the form as it coming back with the error message "You have been detected as a bot..." in the code below.
I printed all the form submissions to a log file and every time it failed, the $recaptcha->success, $recaptcha->action and $recaptcha->score are always empty.
I found that quite often it would work for a user and send the message.
I tested the form myself and most of the time it worked ok, but I noticed if I kept trying the submit the form several times, it would occassionally fail and return the error. When it fails the $recaptcha->success, $recaptcha->action and $recaptcha->score are always empty.
I also noticed $_POST['recaptcha_response'] seems to be empty in these instances even though it passes the first isset if statement.
//verify google captcha v3
if(isset($_POST['recaptcha_response'])){
//build request
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'MY_SECRET_KEY';
$recaptcha_response = $_POST['recaptcha_response'];
//get verify response data
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'secret' => $recaptcha_secret,
'response' => $recaptcha_response
],
CURLOPT_RETURNTRANSFER => true
]);
$output = curl_exec($ch);
curl_close($ch);
$recaptcha = json_decode($output);
// Take action based on the score returned:
if ($recaptcha->success && $recaptcha->action == 'reportadvert' && $recaptcha->score > 0.1){
// Verified - success
$save['captcha'] = "1";
} else {
// Not verified - show form error
$errors['captcha'] = "You have been detected as a bot and blocked from sending this report for security reasons, please try again shortly or Contact us if you are still having issues.";
}
} else {
$errors['captcha'] = "You have been detected as a bot and blocked from sending this report for security reasons, please try again shortly or Contact us if you are still having issues.";
}
Here is My Form (with some of my other fields removed). Please note that my form/page is contained in one php script, and I post the form to the same php script :
<form method="post">
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<button type="submit" name="SendEm">Report Advert</button>
<input type="hidden" name="submitted" value="TRUE" />
</form>
I include the javascrit on the top of the page, I have removed my site key when posting this :
<script src="https://www.google.com/recaptcha/api.js?render=MY_SITE_RECAPTCHA_KEY"></script>
grecaptcha.ready(function() {
grecaptcha.execute('MY_SITE_RECAPTCHA_KEY', {action: 'reportadvert'}).then(function(token) {
var recaptchaResponse = document.getElementById('recaptchaResponse');
recaptchaResponse.value = token;
});
});
I would check error-codes in siteverify response. If there is an error, you will not get action and score params in response.
Site Verify Response, Error code reference

How can I implement Google Recaptcha v3 in a PHP form?

I would like to insert a contact form the new version (V3) of Recaptcha.
I have looked for different solutions, but they only show part of the code, they are incomplete or I get an error, and most of the solutions found are very complicated for something so simple and I do not understand the code.
I have searched this and other forums to implement the new version of ReCaptcha (V3) in my forms.
I needed to know how to:
Insert it with JS
How to validate it with PHP
What new fields were needed in my form.
I did not find any simple solution, which would show me all these points, or it was too complicated for somebody who just wanted to insert a contact form on their website.
At the end, taking some code portions of multiple solutions, I use a simple and reusable code, in which you only have to insert the corresponding keys.
Here it is.
The basic JS code
<script src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"></script>
<script>
grecaptcha.ready(function() {
// do request for recaptcha token
// response is promise with passed token
grecaptcha.execute('your reCAPTCHA site key here', {action:'validate_captcha'})
.then(function(token) {
// add token value to form
document.getElementById('g-recaptcha-response').value = token;
});
});
</script>
The basic HTML code
<form id="form_id" method="post" action="your_action.php">
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
<input type="hidden" name="action" value="validate_captcha">
.... your fields
</form>
The basic PHP code
if(isset($_POST['g-recaptcha-response'])){
$captcha=$_POST['g-recaptcha-response'];
}
else
$captcha = false;
if(!$captcha){
//Do something with error
}
else{
$secret = 'Your secret key here';
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secret."&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']));
if($response->{'success'}==false)
{
//Do something with error
}
}
//... The Captcha is valid you can continue with the rest of your code
//... Add code to filter access using $response . score
if ($response->{'success'}==true && $response->{'score'} <= 0.5) {
//Do something to denied access
}
You have to filter access using the value of $response->{'score'}. It can takes values from 0.0 to 1.0, where 1.0 means the best user interaction with your site and 0.0 the worst interaction (like a bot). You can see some examples of use in ReCaptcha documentation.
You only have to add your keys, no more changes needed:
src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"
grecaptcha.execute('your reCAPTCHA site key here'
and
$secret = 'Your secret key here';
Obviously you also have to change the action of the form, in this example:
action = "your_action.php"
In the answer above, these lines need to be updated in order to be able to read the response values in PHP:
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secret."&response=".$captcha."&remoteip=".$_SERVER['REMOTE_ADDR']));
$response->{'success'}
$response->{'score'}
Looks like Google improved their docs since the first answers. Here is how I do it.
Client side integration in form:
The docs for this are here: https://developers.google.com/recaptcha/docs/v3
According to Google you should include the Recaptcha API on every page so that it can observe the user's behavior. So I added this line to the end of my footer which is included in every page (no parameters needed):
<script src="https://www.google.com/recaptcha/api.js"></script>
On the form you use a submit button like so:
<button class="g-recaptcha" data-sitekey="PASTE-YOUR-RECAPTCHA-SITE-KEY-HERE" data-callback="onSubmit" data-action="submit">Submit Form</button>
And add the following JavaScript function that submits the form:
function onSubmit() {
var form = document.forms[0]; // change this if you have multiple forms
if (/* possible client-side form validation code here */) {
form.submit();
}
}
Server side validating code:
The docs for this are here: https://developers.google.com/recaptcha/docs/verify
For this I created a helper function:
/**
* Checks if the current script has a valid Google Captcha response token.
* #returns True, if the script has a valid repsonse token, otherwise false.
*/
function isCaptchaValid()
{
$captcha = isset($_POST['g-recaptcha-response']) ? $_POST['g-recaptcha-response'] : false;
if (!$captcha) {
return false;
}
$postdata = http_build_query(
array(
"secret" => "PASTE-YOUR-RECAPTCHA-SECRET-KEY-HERE",
"response" => $captcha,
"remoteip" => $_SERVER["REMOTE_ADDR"]
)
);
$opts = array(
'http' =>
array(
"method" => "POST",
"header" => "Content-Type: application/x-www-form-urlencoded",
"content" => $postdata
)
);
$context = stream_context_create($opts);
$googleApiResponse = file_get_contents("https://www.google.com/recaptcha/api/siteverify", false, $context);
if ($googleApiResponse === false) {
return false;
}
$googleApiResponseObject = json_decode($googleApiResponse);
return $googleApiResponseObject->success;
}
No need to check any score value as done in the other answers. According to the docs there isn't even a score property in the response object. I checked it and there is one, but I don't use it.
You should call it at the beginning of the PHP script that handles your form submit like so:
if (!isCaptchaValid()) {
die("STOP! You are a bot."); // or do something else
}

How to organize your code PHP

I am new to PHP and I am wondering how is the best way to organize your code. I have been trying to do something with a form (form.php) on the client side to talk to a remote server using PHP (testexec.php). I have come down to the issue where my testexec.php needs to access a variable from the form.php file and so now I am wondering if I should just put all my code in form.php so I don't have to call variables from a different php file. How would you guys organize your code in this situation.
form.php
<div class="box1">
<form method="post">
<label class="col">Up/Dowb</label>
<span class="col">
<input type="radio" name="option" id="r1" value="1" />
<label for="r1">Up</label>
<input type="radio" name="option" id="r2" value="2" />
<label for="r2">Down</label>
</span>
<span class="col">
<input type="submit" class="button"/>
</span>
</form>
</div>
<script src ="../../../jqueryDir/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(".button").click(function(event){
if ((document.getElementsByName("gateway")[0].value == '')) {
alert('Gateway Required!');
return false;
}
else if (document.querySelectorAll('input[type="radio"]:checked').length < 1) {
alert('Please Choose Up/Down Value!');
return false;
}
else {
//alert('Sucess!');
event.preventDefault();
$.ajax({
url:"testexec.php",
type: "POST",
data: {option: $('input[type=radio]:checked').val()},
dataType: "text",
success:function(result){
$('#div1').html(result)
}
});
return true;
}
});
</script>
<div id="div1"></div>
</body>
</html>
testexec.php
$gateway = '';
$user = 'user';
$pwd = 'pass';
function cleanInput($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
if ( $_SERVER['REQUEST_METHOD'] == 'POST'){
$gateway = cleanInput($_POST['gateway']); //need to get the value of gateway from form.php
//create the ssh connection
if ($connection = #ssh2_connect($gateway, 22)) {
ssh2_auth_password($connection, $user, $pwd);
if(isset($_POST['option']) && $_POST['option'] == 1) {
$stream = ssh2_exec($connection, "/tmp/user/testscripts/up.sh");
stream_set_blocking($stream, true);
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
echo '<pre>' . stream_get_contents($stream_out) . '</pre>';
}
if(isset($_POST['option']) && $_POST['option'] == 2) {
$stream = ssh2_exec($connection, "/tmp/user/testscripts/down.sh");
stream_set_blocking($stream, true);
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
echo nl2br(stream_get_contents($stream_out));
}
}
}
?>
So now i have to somehow get the value of 'gateway' from my form.php for the following code to work:
$gateway = cleanInput($_POST['gateway']);
So I was wondering if this is good practive to seperate things like this?
I see no advantage in combining the scripts. There is no magic to $_POST. It only exists when a script has been the TARGET of a POST, and it doesn't matter if the target is the same script that originally had rendered the form, or a different script.
The only advantage to combining form code into a self-posting, all-in- one version, is when you have iterative error handling.
In that situation, frequently you want to do some server side validation, and if the form doesn't validate, need to send a response with the another form with an error, and usually with the original form elements already filled in, and typically with some visual indication as to which elements caused the problems.
It's much cleaner to have this all in one place, so you aren't reinventing the wheel with the form.
However, moreover, any script is cleaner and easier to read when you separate logic from presentation.
This is a big reason why people use template libraries like smarty or twig, an why every MVC framework comes with some sort of template system.
Even in your case, you could move the form data into it's own seperate script and include it with something like:
require_once('form_frm.php');
In your case, form.php has no logic whatsoever currently, so I see no major advantage to doing this at the moment.
I would recommend however, that you consider each function you are using, and why you are using it.
Stripslashes() for example, appears to have no value to you in this script, and in fact, stripslashes has had very little use for many years now, as magic_quotes_gpc() was deprecated long ago.
Escaping was a function of SQL database string handling, and due to the issues with different character sets and localization, if you needed to add escape characters, there were better database specific methods like mysql_real_escape_string() which take into account the character set of the client data and database.
At this point in time, most everyone knows that you should be using bind variables for adding string data to SQL queries, which essentially eliminates the need for any escaping of quotes, so there is no need for add slashes or mysql_real_escape_string() whatsoever, and the world is better for it.
If you are not calling addslashes(), why are you calling stripslashes()?

No Ajax response even though PHP file set up correctly

I have a simple sign up mailing list form. It sends the user's email address to a store-address.php file. I use jQuery's ajax object to send a request to the php file and then receive a response.
The problem is I am not getting a response from the php file. I tried setting the cache to false in the request. I also tried send the information through the URL like so:
http://www.fifthtribe.com/inc/store-address.php?ajax=true&cache=false&email=test4%40gmail.com
When I do it that way it works and gives me a reponse. But when I do it through ajax it doesn't give me a response. This is from Firebug:
And here's snippets from my code:
HTML:
<div id="mlist">
<form id="mlist_form" method="POST" action="">
<input type="text" id="email" name="email" placeholder="Email" />
<input type="submit" id="submit_btn" value="Join" />
</form>
<div id="response"></div>
</div>
JQuery:
/* Add to mailing list */
$("#mlist_form").submit( function(e){
//$('#response').append('<div id="thanks-mce"><div id="mce-arrow"></div>Thanks for signing up!</div>');
var email = escape( $('#email').val() );
e.preventDefault();
data = {
"ajax" : "true",
"email" : email,
"cache" : "false"
}
$.ajax({
type: "POST",
url: 'inc/store-address.php',
data: data,
success: function( msg ){
// successfully signed up
$('#response').html( msg );
$('#email').val('');
},
error: function( err ){
// error while signing up
$('#response').html('Error: Is your email correct?');
}
});
return false;
});
PHP:
function storeAddress(){
// Validation
if(!$_GET['email']){ return "No email address provided"; }
if(!preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*$/i", $_GET['email'])) {
return "Email address is invalid";
}
require_once('MCAPI.class.php');
// grab an API Key from http://admin.mailchimp.com/account/api/
$api = new MCAPI('xxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us4');
// grab your List's Unique Id by going to http://admin.mailchimp.com/lists/
// Click the "settings" link for the list - the Unique Id is at the bottom of that page.
$list_id = "xxxxxxxx";
if($api->listSubscribe($list_id, $_GET['email'], '') === true) {
// It worked!
return 'Success! Check your email to confirm sign up.';
}else{
// An error ocurred, return error message
return 'Error: ' . $api->errorMessage;
}
}
// If being called via ajax, autorun the function
if($_GET['ajax']){ echo storeAddress(); }
?>
You realize that your PHP script is using GET method but your jQuery code is using the POST method right?
If the information is being posted to PHP, PHP will need to use $_POST to retrieve it. This explains why the URL method using $_GET works but the jQuery POST doesn't.
Good luck!
It looks like you're using $_GET instead of $_POST. Try echoing out the contents of $_REQUEST to see what that holds.
Debug your script!
Place an alert in the success and error parts of your script and then you will know whether the AJAX is working.
If not, you can then work your way up the document and see where the problem is.
In addition, the error here is quite simple. You are using $_GET in PHP and you are POSTING your data using AJAX, this will not show an error. Although the PHP document will not process your request because it is not being fed any parameters.

Entering recaptcha only once on form validation fail?

I have a form which has a few fields and a recaptcha code at the end.
When the user submits the form, the recaptcha field is validated along with the other fields on server side (PHP). If the any of the fields are invalid, the user is redirected to the same form with errors.
However, the problem is : The user has to enter the recatpcha again.
Is there any way I can NOT ask the user to enter the recaptcha again if the form validation fails but captcha validation is successful ?
Sure there is. You could store the validation success of the recaptcha into the session (or a cookie, or a database) and then hide the recaptcha if the indication is there. On the serverside you simply have to check if either the recaptcha is correct or the indication is valid.
You also have to make sure that a valid recaptcha cookie can only be used once, because if not the spammer can simply sent the cookie information over and over again and work around the recaptcha.
My idea is to store a timestamp within the session under a key like "recaptcha_success" and then check if the timestamp is not older than a few minutes (whatever fits your needs). If it's not, work around the recaptcha by not validating it again. If the form is valid, remove the key so the next time the user wants to use the form he has to enter the recaptcha again.
Without seeing your code or how it's set up exactly the simplest thing I can think of is to use a PHP session:
File that receives form data (assumed POST)
session_start();
foreach($_POST as $key=>$val)
{
if($key == "captcha" || $_SESSION['captcha_valid'] != 1)
{
//validate captcha
if(captcha is valid)
{
$_SESSION['captcha_valid'] = 1;
}
}
}
File that contains the form
session_start();
echo "<form method='yourphpfile.php' method='post'>"
if(!isset($_SESSION['captcha_valid']) || $_SESSION['captcha_valid'] != 1)
{
//add captcha code
}
</form>
Yes - set the session variable that captcha was entered successfully and don't display recaptcha form.
you can verify by setting session var like this
<?php
// captcha is already submitted so no need to verify again,
if(!isset($_SESSION['human_signup']) || (time() - $_SESSION['human_signup'] > 300)){
require_once('admin/recaptcha/recaptchalib.php');
// Get a key from https://www.google.com/recaptcha/admin/create
$publickey = 'your publickey';
$privatekey = 'your privatekey';
# the response from reCAPTCHA
$resp = null;
$resp = recaptcha_check_answer ($privatekey,
$_SERVER["REMOTE_ADDR"],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]);
if (!$resp->is_valid) {
// What happens when the CAPTCHA was entered incorrectly
$errors[] = 'The CAPTCHA wasn\'t entered correctly, please try again';
}else{
$_SESSION['human_signup'] = time();
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}
}
and then on html form do something like this to show captcha form.
<?php
if (!isset($_SESSION['human_signup']) || (time() - $_SESSION['human_signup'] > 300)){ ?>
<label class="control-label">CAPTCHA</label>
<script type="text/javascript"> var RecaptchaOptions = {theme : 'white' }; </script>
<script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k=your key"></script>
<noscript>
<iframe src="http://www.google.com/recaptcha/api/noscript?k=your key"
height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40">
</textarea>
<input type="hidden" name="recaptcha_response_field"
value="manual_challenge">
</noscript>
<? } ?>
Make sure you remove the session once user registration is complete. like this.
<?php
//insert registration data in db i.e. registration is complete.
//unset the session used to hide the captcha.
unset($_SESSION['human_signup']);
?>
One less important feature to block user to input captcha manually and then using some script to automatically create accounts.
/* if user tries to register automatically by manually entering captcha and using scrpt to create accoutns */
<?php
if(isset($_SESSION['human_signup']) && ($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT'])) {
unset($_SESSION['human_signup']);
unset($_SESSION['user_agent']);
#session_destroy();
#session_unset();
}
?>
hope this helps., if i missed something , please feel free to edit answer.

Categories