БлогNot. Пишем текстовую капчу на PHP и Javascript

Пишем текстовую капчу на PHP и Javascript

Капча должна быть не сложнее, чем в этом блоге (текстовая строка, размеченная выбранными тегами), но при этом обладать следующими свойствами:

  • нигде не передаваться открыто в данных HTTP;
  • нигде не храниться в незашифрованном виде (в массиве $_SESSION будет находиться только зашифрованная капча);
  • быть по возможности защищённой от ботов, с этой целью раздел документа, содержащий капчу (а в реальном приложении - всю форму ввода, для которой понадобилась капча) будет заполняться данными только после полной загрузки страницы, встраиваясь в цепочку обработчиков события load.

Для работы капчи понадобятся включённые в браузере Javascript и Cookies.

Вот описание порядка действий и основных функций:

  • если данные формы были отправлены (в данных $_POST есть ключ 'ok', соответствующий кнопке типа submit), из массива сессии получается зашифрованный код $valid и сравнивается с тут же зашифрованным числом $code, полученным от пользователя из текстового поля ввода. Если строки совпадают, выводится сообщение 'ok', на самом деле, в этой ветке программы могут выполняться любые нужные нам действия. Если строки не совпадают, выводится 'no' и работа скрипта продолжается, то есть, повторно генерируется новая форма ввода;
  • в список слушателей события load страницы встраивается маленькая безымянная яваскрипт-функция, добавляющая в раздел документа с id="sform" код формы с капчей. Этот код формируется и возвращается PHP-функцией commentform. Для удобства и во избежание проблем с "многострочной" строкой внутри яваскрипта, все разделители в сгенерированном коде формы заменяются на пробелы (функция trim1), так что код формы оказывается вытянутым в одну строку;
  • для непосредственной генерации числа-капчи функция commentform обращается к функции generate_code, которая формирует случайное 4-значное число, шифрует его и сохраняет в данных сессии (значение $valid), а также возвращает видимый код формы $str, содержащий, в том числе, видимую часть капчи, полученную функцией visible_code. Эта последняя функция просто "зашумляет" строку-аргумент тегами, разрешёнными к использованию с помощью массива $tags.

Как и в любых скриптах, работающих с данными сессии, до вывода любой разметки должна быть вызвана стандартная функция session_start. Вот полный код скрипта-примера, его можно выполнить как файл .php на локальном хосте, например, в Denwer. Проверено на PHP 5.5.9.

<?php  session_start(); ?>

<div id="sform"></div>

<?php
 if (isset($_POST['ok'])) {
  $valid='';
  if (isset($_SESSION['valid'])) $valid=$_SESSION['valid'];
  $code = strval(intval($_POST['code'])-1000);
  if (md5($code) == $valid) { 
   echo '<b>ok</b>'; 
   //любые действия, выполняемые при правильном вводе
   exit; 
  }
  else {
   echo '<b>no</b>';
   //при ошибке выводится no и работа скрипта продолжается
  }
 }

 function visible_code ($s) {
  $tags = Array (
   Array ('<span>','</span>'), Array ( '<b>','</b>'), Array ( '<i>','</i>'),
   Array ('<u>','</u>'), Array ('<s>','</s>'), Array ('<sub>','</sub>'),
   Array ('<sup>','</sup>'), Array ('<big>','</big>'), Array ('<small>','</small>')
  );
  $len = strlen ($s);
  $res = '';
  for ($i = 0; $i < $len; $i++) {
   $c = substr ($s, $i, 1);
   $n = rand(0, count($tags)-1);
   $res .= $tags[$n][0].$c.$tags[$n][1];
  }
  return $res;
 }

 function generate_code () {
  srand (time());
  $str = 'Введите код сообщения: ';
  $valid = mt_rand(1000,9999);
  $str .= visible_code ($valid);
  $_SESSION['valid'] = md5 ($valid - 1000);
  $str .= ' <input type="text" name="code" size="5" maxlength="4" value="">';
  return $str;
 }

 function commentform () { 
  return '<form method="post">'.generate_code().
   ' <input type="submit" name="ok"></form>';
 }

 function trim1 ($string) { return preg_replace("/\s+/"," ",trim($string)); }

 echo '<script>
  window.addEventListener("load", function() { 
   document.getElementById("sform").innerHTML = \''.trim1(commentform()).'\'; 
  });
 </script>
 <noscript>Нужен включённый в Вашем браузере Javascript</noscript>';
?>

Вот как выглядит приложение в браузере Firefox:

Текстовая капча в Firefox, скриншот
Текстовая капча в Firefox, скриншот

теги: javascript php программирование безопасность

12.01.2019, 12:43; рейтинг: 258