Материал

Простейшая защита от спама PHP формы отправки данных

Защита от спама PHP формы отправки данных

Реклама - двигатель прогресса. Рекламодатели постоянно ищут все новые способы рекламы своих товаров и услуг. Поэтому не исключено, что через вашу форму будут рассылать СПАМ - так называемый вид рекламной рассылки.

Как защитить форму от отправки таких данных. 100% защиты не может быть, так как никто никогда не запрещает заполнить вашу форму вручную. Но все же мы можем защитить ее от роботов (программ), которые могут отправлять сотни сообщений в секунду.

Создание скрытого поля с шифром при помощи PHP

Простейший способ – создание скрытого поля с генерацией в нем набора спец символов.

Прежде всего, мы добавляем в форму скрытое поле.

<input type="hidden" name="zf" value="<?=$zaschita?>">

То есть в это поле подставляется переменная $zaschita, которую мы должны сгенерировать заранее. Это сделаем следующим образом:

<?php
$zaschita = md5(md5(time())."ivan");
?>

Мы шифруем время запуска формы и имя ivan. Получается, что каждую секунду данная переменная будет изменена, а алгоритм шифрования известен только вам. Вы можете использовать любой другой метод шифрования или использовать набор любых символов вместо ivan. Для того, чтобы обойти такую защиту в вашей форме потребуется разгадать метод шифрования. Поверьте – это возможно, но на это потребуется некоторое время. И, если это произошло, и защита PHP формы отправки данных на почту не работает, то вы всегда можете поменять шифр.

Код формы изменится:

<form action="send.php" method="post">
<input type="text" name="fio" placeholder="Укажите ФИО" required>
<input type="text" name="email" placeholder="Укажите e-mail" required>
<input type="hidden" name="zf" value="<?=$zaschita?>">
<input type="submit" value="Отправить">
</form>

Проверка передаваемого шифра на PHP

Со стороны PHP мы должны принять скрытую переменную и проверить ее тем же образом, но при этом нам обязательно нужно создать новую переменную и зафиксировать в ней время загрузки формы, иначе наша простейшая защита PHP формы от спама будет работать против нас и не даст пользователю отправить сообщение. Для этого создаем переменную, также шифруя ее:

$vremya = md5(time());

Затем прикрепляем переменную при помощи GET массива.

<form action="send.php?p=<?=$vremya?>" method="post">

После этого нам нужно при отправке формы сформировать шифр и сравнить его с тем, который передан нам в скрытом поле.

Получаем данные:

$pol_time = $_GET['p'];
$vernii_shifr = md5($pol_time."ivan");
$pol_shifr = $_POST['zf'];

Сравниваем их. Если совпадают, то отправляем данные на почту, иначе выводим сообщение:

If($vernii_shifr == $pol_shifr){
….
код отправки письма
….
} else {
echo “Не нужно здесь спамить”;
}

Таким образом, полный код PHP формы с защитой от спама выглядит следующим образом:

<html>
<head>
<meta charset="utf-8">
<title>Форма заявки с сайта</title>
</head>
<body>
<?php
$zaschita = md5(md5(time())."ivan");
$vremya = md5(time());
//проверяем, существуют ли переменные в массиве POST
if(!isset($_POST['fio']) and !isset($_POST['email'])){
 ?> <form action="send.php?p=<?=$vremya?>" method="post">
<input type="text" name="fio" placeholder="Укажите ФИО" required>
<input type="text" name="email" placeholder="Укажите e-mail" required>
<input type="hidden" name="zf" value="<?=$zaschita?>">
<input type="submit" value="Отправить">
</form> 
<?php
} else {
 $pol_time = $_GET['p'];
 $vernii_shifr = md5($pol_time."ivan");
 $pol_shifr = $_POST['zf'];
 If($vernii_shifr == $pol_shifr){
               $fio = $_POST['fio'];
               $email = $_POST['email'];
               $fio = htmlspecialchars($fio);
               $email = htmlspecialchars($email);
               $fio = urldecode($fio);
               $email = urldecode($email);
               $fio = trim($fio);
               $email = trim($email);
               if (mail("to@email.ru", "Заявка с сайта", "ФИО:".$fio.". E-mail: ".$email ,"From: ot@email.ru \r\n")){ 
                              echo "Сообщение успешно отправлено";
               } else {
                              echo "При отправке сообщения возникли ошибки";
               }
 } else {
               echo "Не нужно здесь спамить";
 }
}
?>
</body>
</html>

Описание формы отправки данных на почту

Попробуйте посмотреть исходный код данного скрипта в браузере и обновляйте его каждую секунду – вы увидите, что ключи постоянно меняются.

Теперь предлагаю усложнить защиту, т.е. добавить головной боли для спамеров.
Для этого можно создать динамическую переменную в скрытом поле:
<input type="hidden" name="z_<?=$t_vse?>" value="<?=$zaschita?>">
Переменную $t_vse можно создать из времени.
$t1 = substr(time(), 9, 1);
$t2 = substr(time(), 7, 1);
$t3 = substr(time(), 8, 1);
$t_vse = $t1+$t2+$t3;
Например текущее время "1547380408". При помощи скрипта мы берем 10-й, 7-й и 8-й символы и просто их складываем, получая переменную $t_vse.
Далее нужно передать еще одним скрытым полем текущее время,вместо GET запроса, предварительно зашифровав его с возможностью расшифровки:
$kluch = '3287987984';
$shifruem = time();
$kript = mcrypt_create_iv(
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
    MCRYPT_DEV_URANDOM
);
$shifr = base64_encode(
    $kript .
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $kluch, true),
        $shifruem,
        MCRYPT_MODE_CBC,
        $kript
    )
);
Добавляем скрытое поле:
<input type="hidden" name="d" value="<?=$shifr?>">
Перед отправкой данных формы нужно расшифровать переменную d и получить на первый код:
$rsh = $_POST['d'];
$dannie = base64_decode($rsh);
$kript = substr($dannie, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
$nashe_vremya = rtrim(
    mcrypt_decrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $kluch, true),
        substr($dannie, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
        MCRYPT_MODE_CBC,
        $kript
    ),
    "\0"
);
$vernii_shifr = md5(md5($nashe_vremya)."ivan");
 $t1 = substr($nashe_vremya, 9, 1);
 $t2 = substr($nashe_vremya, 7, 1);
 $t3 = substr($nashe_vremya, 8, 1);
 $t_vse = $t1+$t2+$t3;
 $z = 'z_'.$t_vse;
 $pol_shifr = $_POST[$z];
Здесь мы воссоздаем, расшифровывая данные, переменную z_{число}, затем получаем наш первый шифр и сверяем его. Таим образом защита от спама PHP формы была усилена и позволит не использовать поля для ввода символов и т.п., что отпугивает пользователей.
Полный исходный код нашей PHP формы отправки данных с защитой от спама выглядит следующим образом:
<html>
<head>
<meta charset="utf-8">
<title>Форма заявки с сайта</title>
</head>
<body>
<?php
$zaschita = md5(md5(time())."ivan");
$vremya = md5(time());
$kluch = '3287987984';
if(!isset($_POST['fio']) and !isset($_POST['email'])){
$t1 = substr(time(), 9, 1);
$t2 = substr(time(), 7, 1);
$t3 = substr(time(), 8, 1);
$t_vse = $t1+$t2+$t3;
$shifruem = time();
$kript = mcrypt_create_iv(
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
    MCRYPT_DEV_URANDOM
);
$shifr = base64_encode(
    $kript .
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $kluch, true),
        $shifruem,
        MCRYPT_MODE_CBC,
        $kript
    )
);
 ?> <form action="pismo.php" method="post">
<input type="text" name="fio" placeholder="Укажите ФИО" required>
<input type="text" name="email" placeholder="Укажите e-mail" required>
<input type="hidden" name="z_<?=$t_vse?>" value="<?=$zaschita?>">
<input type="hidden" name="d" value="<?=$shifr?>">
<input type="submit" value="Отправить">
</form> 
<?php
} else {
 $rsh = $_POST['d'];
$dannie = base64_decode($rsh);
$kript = substr($dannie, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
$nashe_vremya = rtrim(
    mcrypt_decrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $kluch, true),
        substr($dannie, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
        MCRYPT_MODE_CBC,
        $kript
    ),
    "\0"
);
$vernii_shifr = md5(md5($nashe_vremya)."ivan");
 $t1 = substr($nashe_vremya, 9, 1);
 $t2 = substr($nashe_vremya, 7, 1);
 $t3 = substr($nashe_vremya, 8, 1);
 $t_vse = $t1+$t2+$t3;
 $z = 'z_'.$t_vse;
 $pol_shifr = $_POST[$z];
 if($vernii_shifr == $pol_shifr){
     $fio = $_POST['fio'];
     $email = $_POST['email'];
     $fio = htmlspecialchars($fio);
     $email = htmlspecialchars($email);
     $fio = urldecode($fio);
     $email = urldecode($email);
     $fio = trim($fio);
     $email = trim($email);
     if (mail("to@email.ru", "Заявка с сайта", "ФИО:".$fio.". E-mail: ".$email ,"From: ot@email.ru \r\n")){
 
         echo "Сообщение успешно отправлено"; 
     } else { 
         echo "При отправке сообщения возникли ошибки";
     }
 } else {
     echo "Не нужно здесь спамить";
 }
}
?>
</body>
</html>
Естественно эту защиту можно обойти, если разгадать данный метод, но на это потребуется немало сил и времени.
На рисунке я представил исходный код в браузере, обратите внимание на два скрытых поля. В первом каждую секунду меняется и шифр и название переменной, во втором просто меняется шифр.
исходный код формы отправки данных на почту

На следующем рисунке отображен исходный код через несколько секунд.

исходный код формы отправки данных на почту

Вы всегда можете использовать Google ReCaptcha для защиты от спама. Это более надежная защита.

А по поводу защиты PHP формы от спама, описанной в данной статье, я предлагаю в комментариях описать возможные способы ее обхода для того, чтобы мы могли ее усложнить и сделать более надежной, при этом не перегружая пользователя лишними и раздражающими действиями. Пока главная проблема здесь в том, что все ключи видны пользователю. Можно хранить закрытые ключи в базе данных или банально в файле и это усложнит задачу взлома для любителей СПАМА.