CSRF protection in PHP
Protection against CSRF in PHP must be one of the priorities on your website.
Functionality:
Generally, this is done with a session and a randomly created token. This token will be passed with our form and then compared with the session token. If the session token is different, nothing sent by the form will be processed.
What is a CSRF attack?
CSRF is the acronym for Cross-Site Request Forgery. In this type of attack, attackers can take advantage of active user sessions to execute unauthorized actions on their behalf.
These attacks are generally carried out from other sites, although they could also be done from a desktop app or email. These attacks target the forms on your website.
Note that here we are looking at the attack from the perspective that we are the owners of the site. In other words, something similar to the following is happening:
![](https://blastcoding.com/wp-content/uploads/2024/07/rect13-1.png)
CSRF protection in PHP
Creating a token that always changes ensures that the user is making changes from our page.
To create the token, you could do the following:
$token = bin2hex(random_bytes(32)); // crea 64 caracteres
Generally, the token has an expiration time. You can handle this in the database if you store the token there. For example, Laravel stores the token in the users table, and you could set an expiration time there.
//1 hora o 2 no mas $expires_at = time() + 3600;
Use a hash for the token. Remember that when you encrypt it, it needs to be quick to decrypt but secure. A 256-bit hash will be fine.
This way, we would have 2 additional columns in the users table: token (hash256) (32 bytes) and expires_at (date).
Before continuing, we should consider when the expiration and the token should be checked.
Clearly, the token is checked before processing the form data.
But what about its expiration?
Points to consider:
- If we check the value of expires_at first, we would be accessing the database constantly.
- If we check it in the file that processes the data, it wouldn’t make sense because we would have already submitted the form.
Maybe the token expiration should be stored in a COOKIE or a session, right?
setcookie("TokenExpires", $expires_at, [ 'expires' => $expires_at, 'httponly' => true, 'secure' => true, // al usar HTTPS(el uso de HTTPS es recomendado por google) 'samesite' => 'Strict' ]);
This way, we could keep checking when it expires and regenerate the token.
How do frameworks handle this? For example, Laravel uses sessions for this, in addition to using middlewares to ensure the user is legitimate.
Security considerations for CSRF protection in PHP
Why not just create a cookie with the token and its expiration?
If you were to do that, it wouldn’t be as secure. In fact, it would be even safer to use expires_at
in a session as well.
Still, don’t think sessions are secure on their own; you should consider some things like:
php.ini file#las dos siguientes hoy en dia son absolutamente necesarias session.cookie_httponly=On session.cookie_secure=On #session.use_strict_mode esta desactivado por defecto pero deberias tenerlo en on session.use_strict_mode=On
If they are not already defined, you will need to define them using ini_set
in your PHP project.
One thing you must consider is session ID regeneration. Keep in mind that one of the forms will be the entry point for users to your website.
session_start(); session_regenerate_id(true);
Also, consider that your users may have unstable connections. In such cases, you should delay the regeneration of the ID.
Token and the form
Basically, when you have a form, it should comply with what is shown in the following image.
![](https://blastcoding.com/wp-content/uploads/2024/07/proceso-deseado-fornulario-1.png)
1 – This is the form section where you pass the token in a hidden input field.
2 – Check if the token passed corresponds with the token in the database. If not, the user is redirected without any notice in the first instance.
3 – Data validation is performed. If the data does not meet the requirements, the user is returned to the first instance with error messages.