DEV Community

Syed Saadullah Shah
Syed Saadullah Shah

Posted on • Edited on

Secure Javascript Form Validation with Mootools

Did I get you with the title?

That's right, it's not possible to securely validate a form with Javascript. That is, you can't prevent invalid data from being submitted to the server by using Javascript for validation. We've all seen Javascript-based form validation classes (and classes for Javascript libraries like Mootools, which is my favourite, so I'll be making reference to it here), but it's all too often not made clear enough to those who might use such classes that javascript validation alone provides no security against invalid data being sent to your back-end processing script and making it into your database.

Javascript is executed by the browser, not the server. It allows for all kinds of user-experience enhancements that would not be possible with server-side languages like PHP.

In the context of form validation, javascript allows you to alert the user to an error in his or her input without sending all the data to the form, and presenting a list of errors to the user. This is not only a great thing in terms of convenience to the user, but it can also save you bandwidth in a significant way if you have a high-volume site.

The problem with javascript validation is that it can be circumvented, which means that just as you should never trust user input without validation, you should never rely on client-side validation alone.

The logical approach to validation then is to validate all your content on the server before saving it to the database. What does this mean for you if you want to also have subtle validation messages presented to the user as they fill out the form, not only at the end?

Why duplicate those efforts by writing two sets of functions, one for PHP and one for Javascript?

Personally I prefer to use a single set of validation functions in PHP, and modify my back-end script slightly to call them not only on a full form submit, but on individual XHR requests as well.

This brief tutorial focuses on how you might structure your Javascript and PHP to create two layers of validation that rely on only one set of validation functions, allowing for the security of server-side validation with the convenience and beauty of client-side validation.

Technically the validation is all happening on the back-end, we're simply adding an attractive front-end interface to it, that our users will appreciate. We're not attempting to provide complete code for form validation, instead we're offering an approach for you to build on.

Step 1: build our form, and PHP validation

HTML
For the purpose of this demo, we use a very simple form with one text input.

"<form action="<? echo $_SERVER['PHP_SELF']; ?>" method="get" id="myform">
    Please tell us something you like:
    <input type="text" name="mytext" id="mytext" value="<?=$form['mytext']?>" class="formElement <? if(isset($message['mytext'])){ echo "incorrect"; }; ?>" /><?PHP echo '<span id="msg">'.$message['mytext'].'</span>'; ?> 
    <input type="submit" name="save" id="save" value="Submit" />
</form>
CSS
Two simple classes for the form element to indicate correct or incorrect status. This can be greatly beautified.

<style type="text/css">
    .correct {
        background-color: #b3d483;
    }
    .incorrect {
        background-color: #c76760;
    }
</style>
PHP
Very simple PHP to capture the form submit, and validate it. Since we're using one field, this is extremely basic code. In our example, it sits at the top of the same file as the form.

<?php
//set a default: the form is not complete
$complete = false;

if(isset($_GET['save'])){
    $form = $_GET;
    $message = validate($form);
    if(!$message['error']){
        // if no error, process the form and present a thank you message
        // set "complete" flag to true;
        $complete = true;
    }
}

function validate($form){
    $message = array();
    $message['error'] = false;
    foreach($form as $key=>$value){
        if($key=="mytext"){
            if(strtolower($value) != "peas"){ $message['error'] = true; $message[$key] = "&lt;&lt; Please say \"peas\""; }
            elseif(empty($value)){ $message['error'] = true; $message[$key] = "&lt;&lt; You forgot to tell us what you like!"; }
        }
    }
    return $message;
}
?>"
Enter fullscreen mode Exit fullscreen mode

This is pretty vanilla stuff. We have a form on our page that submits to itself, runs the value of the "mytext" field through a basic validation function, and either processes the input or displays an error. If we add more fields to this, we will obviously have to add to this code to validate each one, and show a list of errors (if any) to the user when the page refreshes.

Now that we have this foundation in place, we can add a little Javascript layer to run this validation on each field asynchronously when its value is changed (tab off the field, or click outside of it). Here we show each code block again, but notice the few extra lines in the HTML, PHP, and the new Javascript added.

Step 2: add the Mootools (Javascript) Layer

This chunk loops through each element of class "formElement," and adds a change event which triggers the asynchronous call to our PHP script. It then interprets the result of the validation and displays a message to the user, or none if there is no error.


<script type="text/javascript" src="mootools-1.2.2-core.js"></script>
var Demo = {
    start: function(){
        $$('.formElement').each(function(el){
            el.addEvent('change',function(e) {
                e.stop();
                var msg = new Element('span').injectAfter(this);
                var request = new Request({
                    url: 'demo.php',
                    link: 'chain',
                    method: 'get',
                    data: {
                        field: this.name,
                        value: this.value,
                        ajax: 1,
                        save: 1
                    },
                    onSuccess: function(response) {
                        var response = JSON.decode(response);
                        if(response.error==false){
                            $('msg').setStyle('visibility','hidden');
                            el.setProperty('class', 'formElement correct' );
                        }else{
                            $('myform').setStyle('visibility','visible');
                            $('msg').set('html',response.mytext);
                            el.setProperty('class', 'formElement incorrect' );
                        }
                    }
                }).send();
            });
        });
    }
}

window.addEvent("domready", function(){
    Demo.start();
});

Enter fullscreen mode Exit fullscreen mode

This is the same as the previous example, except that we add a hidden field called "ajax" (though this is a misnomer.. AJAX stands for "Asynchronous Javascript and XML", which is almost right except for the XML part... our PHP script will return JSON instead. The "ajax" value tells php whether or not it's receiving the input as an asynchronous individual field, or the whole form.

<form action="<? echo $_SERVER['PHP_SELF']; ?>" method="get" id="myform">
    <input type="hidden" name="ajax" id="ajax" value="0" />
    Please tell us something you like:
    <input type="text" name="mytext" id="mytext" value="<?=$form['mytext']?>" class="formElement <? if(isset($message['mytext'])){ echo "incorrect"; }; ?>" /><?PHP echo '<span id="msg">'.$message['mytext'].'</span>'; ?> 
    <input type="submit" name="save" id="save" value="Submit" />
</form>
CSS
This is the same as step 1

<style type="text/css">
    .correct {
        background-color: #b3d483;
    }
    .incorrect {
        background-color: #c76760;
    }
</style>
Enter fullscreen mode Exit fullscreen mode

Here we have added a check for the value of "ajax" which tells the script how to process the input (if it's the whole form, or one field passed asynchronously. All the script needs to do if ajax=1, is output a JSON array and then stop processing, so that it doesn't also return the rest of the HTML on the page.

You might choose to have your forms submit to a separate script, in which case killing the script at this point would be unneccessary. If ajax=1, the validate field now also reorganizes the submitted key and value into an array the same as a form submit would generate so that we don't have to process the input differently. You don't have to do this like this, of course.

<?PHP
$complete = false;

if(isset($_GET['save'])){
    $form = $_GET;
    $message = validate($form);
    if($form['ajax']==1){
        echo json_encode($message);
        die;
    }
    if(!$message['error']){
        // if no error, process the form and present a thank you message
        $complete = true;
    }
}

function validate($form){
    $message = array();
    $message['error'] = false;
    $form = $form['ajax']==1 ? array($form['field']=>$form['value']) : $form;
    foreach($form as $key=>$value){
        if($key=="mytext"){
            if(strtolower($value) != "peas"){ $message['error'] = true; $message[$key] = "&lt;&lt; Please say \"peas\""; }
            elseif(empty($value)){ $message['error'] = true; $message[$key] = "&lt;&lt; You forgot to tell us what you like!"; }
        }
        //other fields we're validating
    }
    return $message;
}
?>
Enter fullscreen mode Exit fullscreen mode

FB Video Downloader

And that's it! With a very small amount of Mootools JS and the addition of 5 or 6 lines to the HTML and PHP, we have a smooth second layer on our validation that saves the user time and frustration if they are having trouble getting their input right.

Now you a trendy front-end without sacrificing security.

Top comments (1)

Collapse
 
lionelrowe profile image
lionel-rowe

I didn't know MooTools was still being maintained tbh. It's most famous as the library that almost broke the web due to its use of monkey patching. To be fair, newer versions of the library don't monkey patch in quite such a dangerous way (the compatibility issues were caused by conditional monkey patching in old versions), but it looks like they still monkey patch using string properties, which can never truly be done safely.