6
\$\begingroup\$

I've a slot booking script which is as follows:

My Current Algorithm

  1. A calendar is shown to the user
  2. The user clicks on a date
  3. I make an ajax call to determine if at least 1 slot is booked on the clicked date
  4. I return back a form from the ajax page ("Time To", "Time From", "Event Name" etc; form along with a hidden input whose value is "1" if at least 1 slot is booked or "0" if no booking is done for that day. This hidden input will be used a flag at a later stage)
  5. The user then enters/selects values in the form and clicks "Book"
  6. Upon the click of the "Book" button, I check the value of the hidden input via javascript
  7. If it is "0", I return true so that the form goes for submission (As there is not even a single slot booked on that day, there is no need to validate if the chosen time slot is booked or not)
  8. If the value is "1", I run another ajax call to validate whether the time slot selected by the user is free, and if not I return false and display message to user stating the slot is already booked

Now this is where my problem arises. Since the value of the hidden input can be easily manipulated through Firebug before the Book button is clicked, I'm wondering what other way can I set the flag which will be tamper proof by the user.

HTML

<div id="calendar">
    <!-- calendar goes here -->
</div>
<div id="booking-form">
    <!-- placeholder to put ajax call's returned data -->
</div>

JavaScript

// executed upon clicking on a date
$("#calendar .day").on("click", function () {
    var clicked_date = $(this).find("input").val();
    $.ajax({
        type: 'post',
        url: 'set-flag.php',
        data: {
            dt: clicked_date
        },
        success: function (data) {
            $("#booking-form").html(data);
        }
    });
});

//executed upon clicking "Book Date"
$("form#booking-form").on("submit", function () 
{
    var flag = $(this).find("input:hidden").val();
    if (flag == 0) // form can be submitted
    {
        return true;
    }
    else 
    {
        $.ajax({
            type: 'post',
            url: 'check-slot-availability.php',
            context:this,
            data: {
                // parameters containing date clicked, time from, and time to values
            },
            success: function (data) {
                if($.trim(data) == 0) // slot already booked
                {
                    // show message to user to select another time slot
                }
                else // form can be submitted
                {
                    this.submit();
                }
            }
        });
    }
    return false;
});

set-flag.php

$clicked_date = safe($_POST['dt']);
/* perform db operations
.
.
.
*/
if($db->num_rows() > 0) // at least 1 slot is booked on selected day
{
   $flag = 1;
}
else
{
   flag = 0;
}

$form = '<form id="booking-form.php" action="some-page.php" method="post">';
$form .= 'Time From : <select><!-- timings go here --></select>';
$form .= 'Time To : <select><!-- timings go here --></select>';
$form .= '<input type="hidden" value="'.$flag.'" />';
$form .= '<input type="submit" value="Book" />';
$form .= '</form>';
echo $form;
exit;
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Never trust the client™. There's nothing that's tamper-proof on the client-side. You can use the client-side to enhance the experience by providing more immediate feedback, but never rely on it. The server has to be the final arbiter anyway. So if someone tampers with it, so what? The booking should still fail once it reaches the server, and the person tampering has achieved nothing. They've, at most, annoyed themselves. \$\endgroup\$
    – Flambino
    Jul 28, 2014 at 10:15
  • \$\begingroup\$ I understand that. But does that mean the way I'm using server data (hidden input value) is the valid way to enhance the user experience? \$\endgroup\$
    – asprin
    Jul 28, 2014 at 10:21
  • 1
    \$\begingroup\$ I'm afraid it doesn't add much here; there's still a request being made before the user get his/her answer. Probably easier to just submit the form via ajax right away, and let the server reply with an "ok" or "not ok" depending on whether the booking succeeded. The extra check that the flag does now doesn't really do anything that the form submission wouldn't also do. Worst case is that the flag-check actually succeeds, but another booking is made milliseconds later, so the form submission fails anyway, despite the extra check. \$\endgroup\$
    – Flambino
    Jul 28, 2014 at 10:55

1 Answer 1

2
\$\begingroup\$

Right now, you have two separate AJAX calls: one to determine if a day has bookings, and one to determine whether a specific time slot can be booked.

I would suggest one of two things:

  1. Send a request only once that returns all booking data for the specified date.
  2. Send only a validation request upon clicking Book; don't bother checking if a date has bookings.

Option 1 is the ideal approach, since it minimises requests. However, if for some reason you don't want a user to know when you're booked, you'll need to go with option 2. Whichever option you go with, you should provide feedback on its success (or failure) after submission, in case somebody else booked it sometime between validation and submission.


If you're adamant about continuing with your approach, then instead of using 0 and 1, consider using a salted hash involving the date. For illustrative purposes, I'm only using one round (since this type of thing doesn't need to be cryptographically secure).

define("BOOKING_DATE_SALT", "asprin111")
$flag = md5($clicked_date . ($db->num_rows() ? "yes" : "no") . BOOKING_DATE_SALT);
...

Of course, you won't want to repeat this code in check-slot-availability.php so it would probably belong better as a function in some shared library.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.