Studocwho
Studocwho

Reputation: 2478

Prevent Ajax form double submitting using form tokens with php

****Update****

There seems to be nothing wrong with my code; since creating a new document with the exact same code totally worked. So the file could have become corrupt somehow. See my answer below.


I can not for the life of me find any related question or problem. Basically, I have a review form that gets sent via Ajax to a php process page. Once validated, inserts the data into a mySQL database. The Ajax sends info, mainly errors back to the user. I need a way to prevent multiple submissions on the server-side using php.

I have tried using form tokens which works without Ajax, but does not with it. The $_SESSION doesn't seem to be carrying over to the form process php page.

The problem... The $_SESSION['review_form_token'] doesn't hold any data at all. If I print the session data in an error, it doesn't print anything at all! However, I have a php captcha that uses sessions and it prints it out with no problems. When I set the session on page load, it will print the token as expected into the hidden input. I don't understand why the form token isn't getting passed to the form process. session_start() is on both documents. I've tried ob_start() to no avail. Please help me debug this.

Here is my simplified setup...

index.php (at the top):

<?php
session_start();
$reviewForm_token = md5(uniqid(rand(), true));
$_SESSION['review_form_token'] = $reviewForm_token;
?>

index.php - Form (somewhere)

<form id="reviewform" method="post" novalidate>
   <input type="text" class="form-control form-style name" name="firstname" size="15" maxlength="40" autocomplete="given-name">
   <input type="text" class="form-control form-style name" name="lastname" size="15" maxlength="40" autocomplete="family-name">                 
    <textarea class="form-control form-style" name="review" minlength="50" required></textarea>                 
    <input type="text" class="form-control form-style" name="captcha" autocomplete="off" maxlength="6" required>
    <img src="https://via.placeholder.com/200x60" id="captcha-review" alt="Review captcha image" width="200" height="60">

    <input type="hidden" name="form_token" value="<?php echo $_SESSION['review_form_token']; ?>">
    <button type="submit" name="submit" class="btn btn-primary submit float-right font">Send</button>
    <button type="reset" class="btn btn-primary reset font">Reset</button>
</form> <!-- End form -->

index.php - Ajax (at the bottom of the document)

$('#reviewform').submit(function(event) {
// Set some variables
var $this = this,
    firstnameInput = $('input[name=firstname]', $this),
    lastnameInput = $('input[name=lastname]', $this),
    nameInput = $('input.name', $this),
    ratingInput = $('input[name=StarRating]', $this),
    ratingInputChecked = $('input[name=StarRating]:checked', $this),
    stars = $('.star-rating', $this),
    reviewInput = $('textarea[name=review]', $this),
    captchaInput = $('input[name=captcha]', $this),
    form_tokenInput = $('input[name=form_token]', $this),
    submitButton = $("button.submit", $this);

// Get the form data
// Must relate to the name attribute...
var formData = {
    'firstname': firstnameInput.val(),
    'lastname': lastnameInput.val(),
    'StarRating': ratingInputChecked.val(),
    'review': reviewInput.val(),
    'captcha': captchaInput.val(),
    'form_token': form_tokenInput.val(),
};
// Process the form
$.ajax({
        type: 'POST', // Define the type of HTTP verb we want to use (POST for our form)
        url: 'formProcess-review.php', // The url where we want to POST
        data: formData, // Our data object
        dataType: 'json', // What type of data do we expect back from the server
        encode: true
    })
    .done(function(data) {
        // Here we will handle errors and validation messages
        if (!data.success) {

            // Handle errors for doublepost (form_token) ---------------
            if (data.errors.doublepost) {
                $('button', $this).parents('.form-row')
                    .append(label + data.errors.doublepost + '</label>')
                    .children('label.invalid').attr('id', 'doublepost-error');
            }
        } else {
            // SUCCESS!!
           // Thanks!
        }
    }); // End .done function
// Stop the form from submitting the normal way and refreshing the page
event.preventDefault();
}); // End .submit function

formProcess-review.php

<?php
session_start();
$errors     = array(); // array to hold validation errors
$data       = array(); // array to pass back data
$firstname  = $_POST['firstname'];
$lastname   = $_POST['lastname'];
$StarRating = $_POST['StarRating'];
$review     = $_POST['review'];
$captcha    = $_POST['captcha'];

// Form Validation...

if ($_POST['form_token'] !== $_SESSION['review_form_token']) {
   $errors['doublepost'] = 'You have already submitted your review, you can not resubmit. If you need to send another review, please reload the page.';
}
// return a response ===========================================================
// if there are any errors in our errors array, return a success boolean of false
if (!empty($errors)) {
   // if there are items in our errors array, return those errors
   $data['success'] = false;
   $data['errors']  = $errors;
} else {
   // if there are no errors process our form, then return a message

   unset($_SESSION['review_form_token']);

   // mySQL inserting data...

   // show a message of success and provide a true success variable
   $data['success'] = true;
   $data['message'] = 'Success!';
}
// return all our data to an AJAX call
echo json_encode($data);
?>

Upvotes: 0

Views: 339

Answers (3)

Studocwho
Studocwho

Reputation: 2478

jun drie's anwser has given me the incentive to create a whole new index.php with the same code; which seemed to work. I don't know why or how but the original file must have become corrupt, forcing it not work properly, but still make it show up on the web (weird, eh?!)

I've edited my code to make more simplistic and hopefully better.

I was having trouble with unsetting the session, so I am now calling session_unset(); straight after session_start(); in the index.php to unset any previous sessions from 1 reload ago.

Though I've made this change, it's very strange that it used to work without it.

Upvotes: 0

jun drie
jun drie

Reputation: 872

I just tried your code and both the Form Token and Session token are being captured correctly on your formProcess-review.php. You're code must have other problems that causes it.

Simplified Ajax submit.

$.ajax({
        type: 'POST', 
        url: 'formProcess-review.php', 
        data: formData, 
        dataType: 'json', 
        encode: true
    })
    .done(function(data) {
  		alert(data.message);
      
    });

Simplified formProcess-review.php

<?php
session_start();
$data['message'] = "Form token = ".$_POST['form_token']."   Session value = ".$_SESSION['review_form_token'];
echo json_encode($data);
die();
?>

Upvotes: 1

Clemen
Clemen

Reputation: 404

Try to add this option 'withCredentials' to the ajax call:

$.ajax({
    type: 'POST', 
    url: 'formProcess-review.php', 
    data: formData, 
    dataType: 'json', 
    encode: true,
    xhrFields: {
      withCredentials: true
    }
}

It's probably that the ajax call not send the session cookie to the server.

Upvotes: 0

Related Questions