Yes, he can. There are actually two issues here:
Send this information as if logged into a user's session:
Send this information arbitrarily without being a logged in user.
In both cases this is called Cross-Site Request Forgery .
There are ways to mitigate or resolve both attacks:
1. Abuse open sessions:
For this you need to ensure that the customer was the one who submitted the form and not an external website. One way to do this is in short, create a random code and protect the cookies used by the session (if you use the default session_start
it uses a cookie as an identifier).
For something minimally secure (and easy to use), then let's go.
First you need to create a session, there are actually other ways, such as sending a custom header, but this will require more 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 cause the cookie to not be fetched by Javascript, due to HttpOnly
as true
, it will only be trafficked in SSL ( Secure
as true
) and still add the samesite=strict
it prevents , in modern browsers, that another site sends requests containing the cookie.
Now we have created the CSRF-Token:
if(!isset($_SESSION['csrf'])){
$_SESSION['csrf'] = pack('H*', random_bytes(24))[1];
}
This will generate 192 random bits, converted to hexadecimal, so that it is easier to insert into forms, although double the size to 48 bytes.
Then, to add in the form:
<input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
Now when the user submits 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 for the attacker ( random_bytes
is sufficient, but can not use time()
or mt_rand()
, for example.)
The user must be using a minimally updated browser, extremely obsolete browsers can let the contents of the page get caught, which would allow the attacker to access the CSRF-Token.
Your website can not be vulnerable to Session Fixation, otherwise the attacker can set a cookie (the session identifier) with a CSRF that he already knows.
This is not the safest method of all, but it is easy to implement. The CSRF should be changed from time to time, or each page a different CSRF token.
This ensures that:
Any other site that can not get the code will not be able to make a valid request.
2. Spam
The above case does not prevent someone from using a cURL of life and makes the request, it is quite simple to ignore the CSRF-Token in these cases.
Assuming that the attacker is not using the browser, he can simply make a request (al curl https://site.com/form.php
) and get 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 sort of Hashcash. This will at least increase the cost for each submission, which should reduce the number of requests made.