Yes, he can. Actually there are two questions here:
Send this information as if you are logged in to a user session:
Send this information arbitrarily without posing as a connected user.
In both cases this is called Cross-Site Request Forgery.
There are ways to mitigate or resolve both attacks:
1. Abusing open sessions:
For this you need to ensure that the customer was the one who submitted the form and not an external website. One of the ways to do this is, in short, to create a random code and protect the cookies used by the session (if you use the standard session_start
he uses a cookie as an identifier).
For something minimally safe (and easy to use), then let’s go.
First you will need to create a session, there are actually other ways, such as sending a custom header, but this will require major changes and is not supported by a simple form. To create the session use:
$default = session_get_cookie_params();
session_set_cookie_params(
$default['lifetime'],
$default['path'] . '; samesite=strict',
$default['domain'],
true,
true
);
session_start();
This will make the cookie unable to be fetched by Javascript due to the HttpOnly
as true
, will only be dealt in SSL (Secure
as true
) and add the samesite=strict
that prevents, in modern browsers, another site from sending requests containing the cookie.
Now we create this CSRF-Token:
if(!isset($_SESSION['csrf'])){
$_SESSION['csrf'] = pack('H*', random_bytes(24))[1];
}
This will generate 192 random bits, converted to hexadecimal, in order to make it easier to insert in the forms, despite doubling the size to 48 bytes.
Then to add to the form:
<input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
Now, when the user submit the form we need to compare it:
if(!isset($_POST['csrf'], $_SESSION['csrf'])){
echo 'Dados não foram enviados';
exit();
}
if(!hash_equals($_SESSION['csrf'], $_POST['csrf'])){
echo 'O "CSRF-Token" está incorreto'
exit();
}
// Chegou aqui está tudo certo.
insere_dados_do_formulario();
// Alteramos o CSRF, para não reutiliza-lo:
$_SESSION['csrf'] = pack('H*', random_bytes(24))[1];
This will be enough to prevent both cases. But there are some criteria for this to be safe:
- The generator must be unpredictable to the attacker (
random_bytes
is sufficient, but cannot use time()
or mt_rand()
, for example.).
- The user must be using a minimally updated browser, extremely obsolete browsers may let pick up the content of the page, which would make the attacker have access to CSRF-Token.
- Your website may not be vulnerable to Session Fixation, otherwise the attacker may set a cookie (the session identifier) with a CSRF he already knows.
This is not the safest method of all, but it is easy to implement. The CSRF must be changed from time to time, or at each page a different CSRF token.
This ensures that:
Any other site that cannot obtain the code will not be able to perform a valid request.
2. Spam
The above case does not prevent someone from using one Curl of life and make the request, it is quite simple to ignore the CSRF-Token in such cases.
Assuming the attacker is not using the browser, he can simply make a request (Alá curl https://site.com/form.php
) and take the CSRF-Token, then make a curl -H "Cookie: phpsessid=ccccc" -d "csrf=aaaaa" https://site.com/form.php
). The values of ccccc
and aaaaa
were obtained in the previous request.
The only way to mitigate this is by using captcha, or some kind of hashcash. This will at least increase the cost for each shipment, which should reduce the number of requests made.