How to protect against Cross Site Request Forgery in web using Synchronizer Token


What is Cross-Site-Request-Forgery ?

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker's choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.

refer : https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)  for more




How do we protect?


In this tutorial what we are going to do is , in brief we generate csrf token for the forms to send/receive requests from the server. Generated token should be synchronized/validated to get access to the server.



Assume that we have two sides such as client side (index.php) and server side (server.php).

Firstly, Start with a client (index.php) create a session and store it in a cookie. 
And also design the login form as given. (*hidden input in form will store value of token)


index.php
 
<?php
     session_start();

     $sessionID = session_id(); //storing session id
 
     setcookie("user_login",$sessionID,time()+3600,"/","localhost",false,true); //cookie terminates after 1 hour
  ?>

<form action="server.php" method="POST">

<input name="user" placeholder="Username" required="required" type="text" />
<input name="pass" placeholder="Password" required="required" type="password" />
<input id="IdOfToken" name="user_csrf" type="hidden" /> 
<button class="btn btn-primary btn-block btn-large" name="submit" type="submit">Let me in.</button>

</form>

now we set the "sessionID" to the cookie named "user_login" and it will expire after 1 hour.


At the same time we need to start session on server side and create keyed hash value for csrf token. therefore,




server.php
 
<?php
     session_start(); //server session starts
     generateToken(); //create and store token in to a session array


//create csrf token
function generateToken(){

    if(empty($_SESSION['key']))
    {
        $_SESSION['key']=bin2hex(random_bytes(32));       
    }

    $token = hash_hmac('sha256',"token for user login",$_SESSION['key']);
    $_SESSION['CSRF_TOKEN'] = $token;

    ob_start(); //store  in buffer
    
}

Here we have created CSRF token and stored in server side session array named "CSRF_TOKEN" and also stored in buffer memory.

After that we have to get token from client side (index.php) to fetch it with server side. So that I run AJAX callback function loadDoc() to send session id as token. (AJAX call : https://www.w3schools.com/xml/ajax_intro.asp)




loadDoc() Function JavaScript


function loadDOC(method,url,htmlTag)
{
    var xhttp = new XMLHttpRequest(); 
    xhttp.onreadystatechange = function() 
    {
        if(this.readyState==4 && this.status==200)
        {           
            document.getElementById(htmlTag).value = this.responseText;
                   
        }
    };

    xhttp.open(method,url,true);
    xhttp.send();
}


calling loadDoc() in client Side (Index.php)

<?php 
    //ajax call
       if(isset($_COOKIE['user_login']))
            { 
                echo '<script> var token = loadDOC("POST","server.php","IdOfToken");  </script>'; 
                        
            }
    ?>
   

Now token has sent to the server side via ajax call. Finally, I implemented authorization part (through function "validate") which validates csrf token value and other input details whenever user clicks the submit button.

server.php


if(isset($_POST['submit']))
{
    ob_end_clean(); //clean outer buffer memory
    
    validate($_POST['user'],$_POST['pass'],$_POST['user_csrf'],$_COOKIE['user_login']);

}
   

function validate($username, $password,$user_token,$user_sessionCookie)
{

    if($username=="admin" && $password=="admin")
    {
        if($user_token==$_SESSION['CSRF_TOKEN'] && $user_sessionCookie==session_id())
        {
            echo "<script> alert('Logged in Successfully') </script>";
            echo "<h1 style=\"font-size:50px;text-align:center;\">Welcome : ".$username."<br/></h1>";
            
            
        }
        else
        {
           echo "<script> alert('Login failed! CSRFToken not matching!!') </script>"; 
     
           echo "<script type=\"text/javascript\"> window.location.href = 'index.php'; </script>";       
        }   
        
    }
    else{
        echo "<script> alert('Login failed! Check your username, password and login again!!') </script>"; 
           
        echo "<script javascript="" text="" type="\"> window.location.href = 'index.php'; </script>";

    }

    
}
 
validate function i check the username, password and after that I compare "user_csrf" token from hidden input field to the token "CSRF_TOKEN" which we created at the beginning and checked sessionId in both which stored in cookie.

After the successfull login, it will show server page otherwise , it will be redirected to the client page (index.php).

0 comments:

Post a Comment