Joe Bobby
Joe Bobby

Reputation: 2811

Implementing Google ReCAPTCHA V3 Into Website (PHP)

I'm trying to implement Google ReCAPTCHA V3 using the PHP example here: https://stackoverflow.com/a/60036326/1096190

Yesterday's output was 0.9 but form.js still had issues sending the form. Today, It seems like I am getting a score of 0.3 and its giving me an error.. I'm thinking maybe because I have submitted the form a few times maybe? Either way, it wasn't working correctly yesterday and wanted to know if anyone can spot something that I have done incorrectly so I can implement and then test with another IP address. Heres what I have so far:

Yesterday's Output:

mail.php - print_r($response)

{ "success": true, "challenge_ts": "2024-03-05T08:42:59Z", "hostname": "fakedomain.com", "score": 0.9, "action": "homepage" }

form.js

Something went wrong, your message could not be sent.


Today, after more testing I am getting:

mail.php - print_r($response)

{ "success": true, "challenge_ts": "2024-03-06T20:58:07Z", "hostname": "fakedomain.com", "score": 0.3, "action": "homepage" }

form.js

Error! The security token has expired or you are a bot.


What I Have So Far:

HTML File
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Contact</title>
    <link rel="stylesheet" href="/assets/css/style.css" />
    
    
    <!-- Google Recaptcha V3 Begin -->
    <script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA-site-key-goes-here"></script>
    <!-- Google Recaptcha V3 End -->


</head>
<body>
    <section>
        <div>
            <div class="card">
                <h2 class="card-header">Contact Form</h2>
                <div class="card-body">
                    <form class="contact_form" method="post" action="mail.php">
                        <div class="row">
                            <div class="col-md-6 form-group">
                                <input name="name" type="text" class="form-control" placeholder="Name" required>
                            </div>
                            <div class="col-md-6 form-group">
                                <input name="email" type="email" class="form-control" placeholder="Email" required>
                            </div>
                            <div class="col-md-6 form-group">
                                <input name="phone" type="text" class="form-control" placeholder="Phone" required>
                            </div>
                            <div class="col-md-6 form-group">
                                <input name="subject" type="text" class="form-control" placeholder="Subject" required>
                            </div>
                            <div class="col-12 form-group">
                                <textarea name="message" class="form-control" rows="5" placeholder="Message" required></textarea>
                            </div>
                            <div class="row">
                                <div class="col-12">
                                    <div class="contact_msg" style="display: none">
                                        <p>Your message was sent.</p>
                                    </div>
                                </div>
                            </div>
                            <div class="col-12">
                                <input type="submit" value="Submit Form" class="btn btn-success" name="post">
                            </div>
                            <input type="hidden" id="token" name="token">
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>


    <!-- Google Recaptcha V3 Script Begin -->
    <script>
    grecaptcha.ready(function() {
        grecaptcha.execute('reCAPTCHA-site-key-goes-here', {action: 'homepage'}).then(function(token) {
        // console.log(token);
        document.getElementById("token").value = token;
        });
        // refresh token every minute to prevent expiration
        setInterval(function(){
        grecaptcha.execute('reCAPTCHA-site-key-goes-here', {action: 'homepage'}).then(function(token) {
            console.log( 'refreshed token:', token );
            document.getElementById("token").value = token;
        });
        }, 60000);
    });
    </script>
    <!-- Google Recaptcha V3 Script End -->
    

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>


    <!-- form.js Begin -->
    <script src="form.js"></script>
    <!-- form.js End -->


</body>
</html>
mail.php
<?php

if ($_SERVER["REQUEST_METHOD"] == "POST") {

  # BEGIN Setting reCaptcha v3 validation data
  $url = "https://www.google.com/recaptcha/api/siteverify";
  $data = [
    'secret' => "reCAPTCHA-secret-key-goes-here",
    'response' => $_POST['token'],
    'remoteip' => $_SERVER['REMOTE_ADDR']
  ];

  $options = array(
    'http' => array(
      'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
      'method'  => 'POST',
      'content' => http_build_query($data)
    )
    );
  
  # Creates and returns stream context with options supplied in options preset 
  $context  = stream_context_create($options);
  # file_get_contents() is the preferred way to read the contents of a file into a string
  $response = file_get_contents($url, false, $context);
  # Takes a JSON encoded string and converts it into a PHP variable
  $res = json_decode($response, true);
  # END setting reCaptcha v3 validation data
   
    print_r($response); 
# Post form OR output alert and bypass post if false. NOTE: score conditional is optional
# since the successful score default is set at >= 0.5 by Google. Some developers want to
# be able to control score result conditions, so I included that in this example.

  if ($res['success'] == true && $res['score'] >= 0.5) {
 
    # Recipient email
    $mail_to = "[email protected]";
    
    # Sender form data
    $subject = trim($_POST["subject"]);
    $name = str_replace(array("\r","\n"),array(" "," ") , strip_tags(trim($_POST["name"])));
    $email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
    $phone = trim($_POST["phone"]);
    $message = trim($_POST["message"]);
    
    if (empty($name) OR !filter_var($email, FILTER_VALIDATE_EMAIL) OR empty($phone) OR empty($subject) OR empty($message)) {
      # Set a 400 (bad request) response code and exit
      http_response_code(400);
      echo '<p class="alert-warning">Please complete the form and try again.</p>';
      exit;
    }

    # Mail content
    $content = "Name: $name\n";
    $content .= "Email: $email\n\n";
    $content .= "Phone: $phone\n";
    $content .= "Message:\n$message\n";

    # Email headers
    $headers = "From: $name <$email>";

    # Send the email
    $success = mail($mail_to, $subject, $content, $headers);
    
    if ($success) {
      # Set a 200 (okay) response code
      http_response_code(200);
      echo '<p class="alert alert-success">Thank You! Your message has been successfully sent.</p>';
    } else {
      # Set a 500 (internal server error) response code
      http_response_code(500);
      echo '<p class="alert alert-warning">Something went wrong, your message could not be sent.</p>';
    }   

  } else {

    echo '<div class="alert alert-danger">
        Error! The security token has expired or you are a bot.
       </div>';
  }  

} else {
  # Not a POST request, set a 403 (forbidden) response code
  http_response_code(403);
  echo '<p class="alert-warning">There was a problem with your submission, please try again.</p>';
} ?>
form.js
(function ($) {
'use strict';

var form = $('.contact_form'),
  message = $('.contact_msg'),
  form_data;

// Success function
function done_func(response) {
  message.fadeIn()
  message.html(response);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
  form.find('input:not([type="submit"]), textarea').val('');
}

// fail function
function fail_func(data) {
  message.fadeIn()
  message.html(data.responseText);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
}

form.submit(function (e) {
  e.preventDefault();
  form_data = $(this).serialize();
  $.ajax({
    type: 'POST',
    url: form.attr('action'),
    data: form_data
  })
  .done(done_func)
  .fail(fail_func);
}); })(jQuery);

Upvotes: 0

Views: 153

Answers (0)

Related Questions