In this tutorial, you can learn to prevent your site from CSRF or Cross-site Request Forgery Attacks using PHP Language. The tutorial aims to provide students and beginners with a reference for learning to build a secure web application using PHP Language for the server-side script. Here, I will be providing a simple web page script that demonstrates the main Idea for preventing the site from CSRF Attacks.
What is CSRF Attack?
The CSRF or Cross-site Request Forgery is a type of attack that involves a hacker compelling you to take action against a website you are currently logged in to. Failure of protecting the site from CSRF attacks gives an attacker the ability to partially get around the same origin policy, which is meant to stop various websites from interfering with one another. An attacker can cause your client to carry out an unintentional action.
How to Prevent CSRF Attacks in PHP?
We can prevent the CSRF Attack by implementing a CSRF Token validation. The CSRF Token is a generated random token that will be used for validating the form data or the request was exactly submitted from the right form page of the client side. We can set up also the CSRF Token that regenerates every time the form page loads to make it more secure and we can also add an expiration so that the attackers will only have a limited and short time to bypass it. Check out the sample web page that I created and provided below to have a better idea of how you can prevent your site from CSRF Attach using PHP.
Sample Web Page
The scripts below result in a simple web application that contains a sample form that requires the user's basic information and contacts. The form data contains a generated CSRF Token to allow the server API to validate the request. The application also has a sample file script that simulates form requests without the CSRF Token.
Form Page Interface
Here is the PHP file script named index.php. It contains a combined PHP and HTML code which are the PHP Generated CSRF Token stored in SESSION Global Variable and the page/form layout elements.
<?php
session_start();
if(isset($_SESSION['csrf_token']['sample-form']))
unset($_SESSION['csrf_token']['sample-form']);
$_SESSION['csrf_token']['sample-form']['token'] = password_hash(uniqid(mt_rand(), true),PASSWORD_DEFAULT);
$_SESSION['csrf_token']['sample-form']['expiry'] = time() + 3600 ;
?>
<!DOCTYPE html>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" />
<link rel="stylesheet" href="style.css">
<div class="content-md-lg py-3">
<div class="col-lg-8 col-md-10 col-sm-12 col-12 mx-auto">
<div class="page-title">CSRF Token Protection in PHP
</div>
<hr style="margin:auto; width:25px" class="border-light opacity-100">
<div class="container-lg">
<div class="row py-3 my-5">
<div class="col-lg-5 col-md-5 col-sm-10 col-12 mx-auto">
<!-- Sample Web Form -->
<div class="card rounded-0">
<div class="card-header rounded-0">
<div class="card-title fw-bold">Sample Web Form
</div>
<div class="card-body rounded-0 py-2">
<div class="container-fluid">
<form action="api.php" method="POST">
<input type="hidden" name="token" value="<?= $_SESSION['csrf_token']['sample-form']['token'] ?>">
<label for="fullname" class="form-label">Fullname
</label>
<input type="text" id="fullname" name="fullname" class="form-control rounded-0" required="required">
<label for="email" class="form-label">Email
</label>
<input type="email" id="email" name="email" class="form-control rounded-0" required="required">
<label for="phone" class="form-label">Phone
</label>
<input type="text" id="phone" name="phone" class="form-control rounded-0" required="required">
<div class="mx-auto col-lg-6 col-md-8 col-sm-12">
<button class="btn btn-sm rounded-pill btn-primary w-100">Submit
</button>
<div class="mx-auto col-lg-6 col-md-8 col-sm-12 text-center">
<a href="./insecure-request.php" target="_blank">Simulate Attacker Request
</a>
<!-- Sample Web Form -->
<footer class="footer py-2 position-fixed bg-black text-light bottom-0 w-100">
<div class="text-center"><b>This tutorial program is developed by:
</b></div>
API
Here is the PHP file script known as api.php. It is the file script where the form data is being processed or action is being made. The script contains the security check validation with 3 levels. The first level is for checking if there's a valid CSRF token. The second level checks if the request token matches the valid token, and the last level check if the token is already expired.
<?php
//First Level Security Check
if(isset($_SESSION['csrf_token']['sample-form'])){
//First Level Security Check is Validated
// Valid Token Data
$valid_token = $_SESSION['csrf_token']['sample-form']['token'];
$token_expiry = $_SESSION['csrf_token']['sample-form']['expiry'];
// POST Data
$token = $_POST['token'];
// Escape Form Fields Value
//Second Level Security Check
if($token === $valid_token){
// Second Level Security Check Validated
//Third Level Security Check
if(time() < $token_expiry){
// Third Level Security Check Validated
print("<h1>Request has passed the validation.</h1>");
print("<h3>Submitted Data</h2>");
print("<div><b>Fullname</b>: ${fullname}<div>");
print("<div><b>Email</b>: ${email}<div>");
print("<div><b>Phone</b>: ${phone}<div>");
}else{
// Failed in Third Level Security
print_r("Third Level Security Check Failed: Token has expired. Kindly refresh the form page and retry your submission.");
}
}else{
// Failed in Second Level Security
print_r("Second Level Security Check Failed: Server does not allow you to proceed to the process/action.");
}
}else{
// Failed in First Level Security
print_r("First Level Security Check Failed: Server does not allow you to proceed to the process/action.");
}
print("<br/><a href='./'>Back to Form Page</a>");
?>
Sample Bypass Script
Here is an example PHP file script known as insecure-request.php. It contains the sample script that bypasses the form request without CSRF Token.
<?php
curl_setopt($ch, CURLOPT_URL
,"http://localhost/php-csrf-protection/api.php");
"fullname=Malicious Value&email=Malicious Value&phone=Malicious Value");
echo($output);
?>
Snapshots
Here are some of the web page snapshots as the overall result of the scripts I provided above.
Form Page Interface
Secure Request-Response Message
Insecure Request-Response Message
Invalid CSRF Token
Expired CSRF Token
DEMO VIDEO
There you go! I have also provided the complete source code zip file of the scripts that I provided above on this site and it is free to download. You can download it by clicking the download button located below this tutorial's content. Feel free to download and modify it to do some experiments.
That's it! I hope Preventing CSRF (Cross-Site Request Forgery) Attack in PHP Tutorial will help you with what you are looking for and will be useful for your current and future PHP Projects.
Explore more on this website for more Tutorials and Free Source Codes.
Happy Coding =)