Why client side validation is not a good idea or: how i managed to save about 20 hours of studying

Why client side validation is not a good idea or: how i managed to save about 20 hours of studying

Hi, it's me again, this story begins one month ago: My automation teacher assigned me a quiz, made of 20 hours of lessons. I didn't have enough time to do that, since in these days i had to deal with the insane amount of tests and homeworks, and that takes time, so i ended up procrastinating until i realized that the deadline was close by 11 days

How i did?

After receiving the credentials, i logged up the website

and took the first questions

then, i inspected the html to see what's happening behind the scenes

every checkbox is composed by a value and calls a javascript function called "next()" so i digged up a bit to see what this function does

var tot_test = 0; //inizializate this variable that keeps track of the total score                            
                    function next(nr) //declaration of next function that accepts a string as input
                    {
                        if(document.getElementById('d'+nr+'-a').checked == true){ //get the checkbox with class 'd'+input value+a
                            tot_test += parseInt(document.getElementById('d'+nr+'-a').value); // get the value of this element and parse that value as integer if that value is "1" the variable is incremented else no
                        }
                        if(document.getElementById('d'+nr+'-b').checked == true){
                            tot_test += parseInt(document.getElementById('d'+nr+'-b').value);
                        } 
                        if(document.getElementById('d'+nr+'-c').checked == true){
                            tot_test += parseInt(document.getElementById('d'+nr+'-c').value);
                        }
                        if(document.getElementById('d'+nr+'-d').checked == true){
                            tot_test += parseInt(document.getElementById('d'+nr+'-d').value);
                        }

                        //-----------------------------------------------
                        soglia_min = parseInt('8'); // here is the max amount of right answer i have to do in order to pass the test
                        nmax = parseInt('10'); // here is the number of questions
                        //-----------------------------------------------

                        for(i=1; i<=nmax; i++){
                            document.getElementById('d'+i).style.display = 'none';
                            if(document.getElementById('d-audio'+i)){
                                document.getElementById('d-audio'+i).pause();
                            }
                        }

                        nr_succ = parseInt(nr) + 1;
                        if(nr_succ == nmax+1)
                        {

                                if(tot_test == nmax || (soglia_min > 0 && tot_test >= soglia_min)){ // if i have 8 or more correct answers
                                    document.getElementById('loader').style.display='block';
                                    document.getElementById('form-s').submit(); // send the form to the backend
                                }else{
                                    if(soglia_min > 0){
                                                                                    alert('Hai risposto correttamente solo a '+tot_test+' domande su '+nmax+'.\nPer proseguire devi rispondere correttamente ad almeno '+soglia_min+' domande. Riprova');
                                                                            }else{
                                                                                    alert('Hai risposto correttamente solo a '+tot_test+' domande su '+nmax+'.\nPer proseguire devi rispondere correttamente a tutte le domande. Riprova');    // otherwise output the error message
                                                                            }
                                    tot_test = 0;
                                    for(i=1; i<=nmax; i++){
                                        document.getElementById('d'+i+'-a').checked = false;
                                        document.getElementById('d'+i+'-b').checked = false;
                                        document.getElementById('d'+i+'-c').checked = false;
                                        document.getElementById('d'+i+'-d').checked = false; //reset the selections
                                    }
                                    document.getElementById('d1').style.display = 'block';
                                    document.getElementById('d'+nmax).style.display = 'none'; // takes me back to the main screen
                                }

                                                    }
                        else
                        {
                            document.getElementById('d'+nr_succ).style.display = 'block'; //show me the correct answers
                        }
                    }

After that we know how this function works, we realize that would be pointless to override this function in order to accept less answers because the backend will reject the entire form so how we can do?

Wrong validation

We discovered that all the answers are in the same page hidden, so we can inspect the html page to determine what is the good answer if the value is equal to zero, the answer is not correct otherwise it is.

So i ended up creating a simple tool that iterates over all the forms element seeking all the input that are value="1", returning the text inside

<?php
function getValuesInsidePTags($html) {
    $dom = new DOMDocument();
    $dom->loadHTML($html);

    $occurrences = array();

    $inputElements = $dom->getElementsByTagName('input');
    foreach ($inputElements as $input) {
        $value = $input->getAttribute('value');
        if ($value === '1') {
            $pTag = $input->parentNode;
            if ($pTag->nodeName === 'p') {
                $occurrences[] = $pTag->nodeValue;
            }
        }
    }

    return $occurrences;
}

Conclusions

When you are making a quiz website avoid this approach, use server side validation instead. A backend dev is more expensive but at least you don't get fucked by a 16 y.o guy