Рубрика «PHP»

Защитное изображение для HTML-формы

Для защиты от автоматической регистрации в HTML-форму добавляют изображение со случайной последовательностью цифр и букв, которые выводятся пользователю в виде динамического изображения. Одновременно в сессию помещается случайная последовательность цифр. Пользователь должен ввести код, который он видит на изображении, в текстовое поле. Если код совпадает с сохраненным в сессии, можно с большой долей вероятности сказать, что сайт просматривает человек, поскольку робот обычно не решает задачу распознавания образов.

<?php

// Ширина изображения
$width = 150;
// Высота изображения
$height = 50;
// Количество символов в коде
$sign = 5;
// Защитный код
$code = '';
 
// Стартуем сессию
session_start();
 
// Символы, используемые в коде
$letters = array('A','B','C','D','E','F','G','H','J','K','M','N',
                 'P','Q','R','S','T','U','V','W','X','Y','Z',
                 '2','3','4','5','6','7','8','9');
// Компоненты для RGB-цвета
$figures = array(70, 90, 110, 130, 150, 170, 190);
// Создаем пустое изображение
$img = imagecreatetruecolor($width, $height);
 
// Заливаем фон белым цветом
$fon = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $fon);

// Ширина поля под один символ
$letterWidth = intval((0.9*$width)/$sign);

// Заливаем фон точками
for($j=0; $j<$width; $j++) {
  for($i=0; $i<($height*$width)/600; $i++) {
    // Формируем случайный цвет
    $color = imagecolorallocatealpha(
             $img,
             $figures[rand(0,count($figures)-1)],
             $figures[rand(0,count($figures)-1)],
             $figures[rand(0,count($figures)-1)],
             rand(10,30));
    // Устанавливаем случайную точку случайного цвета
    imagesetpixel($img, rand(0,$width), rand(0,$height), $color);
  }
}

// Накладываем защитный код
for($i=0; $i<$sign; $i++)
{
  //Рисуем
  $color = imagecolorallocatealpha(
           $img,
           $figures[rand(0,count($figures)-1)],
           $figures[rand(0,count($figures)-1)],
           $figures[rand(0,count($figures)-1)],
           rand(10,30));

  // Генерируем случайный символ
  $letter = $letters[rand(0,sizeof($letters)-1)];

  // Формируем координаты для вывода символа
  if( empty($x) ) {
    $x = intval($letterWidth*0.2);
  } else {
    if( rand(0,1) )
      $x = $x + $letterWidth + rand(0, intval($letterWidth*0.1));
    else
      $x = $x + $letterWidth - rand(0, intval($letterWidth*0.1));
  }
  $y = rand( intval($height*0.7), intval($height*0.8) );
 
  // Размер шрифта символа
  $size = rand( intval(0.4*$height), intval(0.5*$height) );
  // Угол поворота символа
  $angle = rand(0, 30) - 15;
  // Запоминаем символ в переменной $code
  $code .= $letter;
  // Выводим символ на изображение
  imagettftext($img, $size, $angle, $x, $y, $color, "arialbd.ttf", $letter);
}
 
// Помещаем защитный код в сессию
$_SESSION['code'] = $code;
 
// Выводим изображение
header ("Content-type: image/jpeg");
imagejpeg($img);

?>

Скрипт позволяет создать изображение шириной $width и высотой $height пикселов, содержащее $sign случайных символов случайного цвета. Символы задаются массивом $letters, компоненты цвета — массивом $figures. После этого изображение заливается белым цветом, на котором размещаются в случайном порядке множество цветных пикселов, а затем генерируется и выводится случайный код $code. Переменная $code, помимо всего прочего, помещается в сессию, для того чтобы обработчик формы имел возможность сравнить введенный пользователем и сгенерированный коды. Так как данные из сессии не покидают сервер, злоумышленники не имеют возможности украсть защищенный код, сталкиваясь со сложной задачей распознавания образов.

Пример использования:

<?php
// Стартуем сессию
session_start();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Пример защиты формы</title>
</head>
<body>
<?php
if(isset($_POST['code']) && isset($_SESSION['code'])) {
  if($_POST['code'] == $_SESSION['code'])
    echo '<p style="color:green">Защитный код верен!</p>';
  else
    echo '<p style="color:red">Неверный защитный код!</p>';
  unset( $_SESSION['code'] );
} else {
  echo '<form action="'.$_SERVER['PHP_SELF'].'" method="post">';
  echo '<img src="captcha.php" border="0" alt="" /><br/>';
  echo '<input type="text" name="code" value="" /><br/>';
  echo '<input type="submit" name="submit" value="Отправить" />';
  echo '</form>';
}
?>
</body>
</html>

Ссылки по теме:

Построение диаграмм средствами GDLib

Построение гистограммы

Пусть значения столбцов задаются в процентах от 0 до 100 в массиве $rows:

<?php
// Значение столбцов от 0 до 100
$rows = array(80, 75, 53, 32, 20);

// Ширина изображения
$width = 200;
// Высота изображения
$height = 200;
// Ширина одного столбца
$rowWidth = 30;
// Ширина интервала между столбцами
$rowInterval = 5;

// Создаем пустое изображение
$img = imagecreatetruecolor($width, $height);

// Заливаем изображение белым цветом
$white = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $white);

for($i = 0, $y1 = $height, $x1 = 0; $i < count($rows); $i++) {
  // Формируем случайный цвет для каждого из столбца
  $color = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
  // Нормирование высоты столбца
  $y2 = $y1 - $rows[$i]*$height/100;
  // Определение второй координаты столбца
  $x2 = $x1 + $rowWidth;
  // Отрисовываем столбец
  imagefilledrectangle($img, $x1, $y1, $x2, $y2, $color);
  // Между столбцами создаем интервал в $row_interval пикселей
  $x1 = $x2 + $rowInterval;
}

// Выводим изображение в браузер, в формате GIF
header ("Content-type: image/gif");
imagegif($img);
?>

Результат работы скрипта:

Как видите, скрипт использует значения элементов массива $rows как процентные величины для формирования в цикле for гистограммы. На каждой итерации цикла формируется случайный цвет, который используется для отрисовки очередного столбца гистограммы. Столбец формируется при помощи функции imagefilledrectangle(), которая рисует заполненный прямоугольник.

Построение круговой диаграммы

Пусть доли секторов заданы массивом $row из предыдущего скрипта. При построении круговой диаграммы следут помнить, что в круге 360 градусов, и значения массива $rows следут нормировать таким образом, чтобы их сумма равнялась 360 градусам.

<?php
// Значения столбцов от 0 до 100
$rows = array(80, 75, 53, 32, 20);
// Нормируем значения массива $rows таким образом,
// чтобы их сумма составляла 360 градусов
$sum = array_sum($rows);
for($i = 0; $i < count($rows); $i++) {
  $rows[$i] = intval( round($rows[$i]*360/$sum) );
}

// Создаем пустое изображение размером 201x201 пикселей
$img =  imagecreatetruecolor(201, 201);
// Определение белого цвет на изображении
$white = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $white);

// Переменные $cx и $cy определяют центр круговой диаграммы
$cx = $cy = 100;
// Переменные $w и $h определяют ширину и высоту диаграммы
$w = $h = 200;

$start = 0;
foreach ($rows as $value) {
  // Формируем случайный цвет для каждого сектора
  $color = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
  // Определяем конечный угол сектора
  $angle_sector = $start + $value;
  // Отрисовываем сектор
  imagefilledarc($img, $cx, $cy, $w, $h, $start, $angle_sector, $color, "IMG_ARC_PIE || IMG_ARC_EDGED");
  // Увеличиваем значение начального угла сектора
  $start += $value;
}
// Вывод изображения в окно браузера
header ("Content-type: image/gif");
imagegif($img);                     
?>

Результат работы скрипта:

Отправка данных из скрипта методом POST

Пусть у нас есть простая форма, состоящая из двух полей и кнопки для отправки данных:

<form action="action.php" method="post">
Имя: <input name="name" type="text" /><br/>
Пароль: <input name="password" type="password" /><br/>
<input name="submit" type="submit" value="Отправить" />
</form>

Обработчик формы action.php выводит в браузер текст, введенный в поля формы:

<?php
echo 'Имя: '.$_POST['name'].'<br/>';
echo 'Пароль: '.$_POST['password'].'<br/>';
?>

HTML форма позволяет пользователю сформировать POST-запрос, который затем отсылается браузером. Мы сформируем такой запрос скриптом.

Метод POST, в отличие от метода GET, посылает данные не в строке запроса, а в области данных, после заголовков. Передача данных аналогична методу GET: группы name=value объединяются при помощи амперсанда (&):

name=Евгений&password=qwerty

Кроме того, необходимо учитывать, что данные передаются в текстовом виде, поэтому все национальные символы следует подвергать кодированию при помощи функции urlencode.

Отправка данных методом POST через сокеты

<?php
$hostname = 'localhost';
$path = '/handler/action.php';
$content = '';
// Устанавливаем соединение с сервером $hostname
$fp = fsockopen($hostname, 80, $errno, $errstr, 30);
// Проверяем успешность установки соединения
if (!$fp) die('<p>'.$errstr.' ('.$errno.')</p>');

// Данные HTTP-запроса
$data = 'name='.urlencode('Евгений').'&password='.urlencode('qwerty');
// Заголовок HTTP-запроса
$headers = 'POST '.$path." HTTP/1.1\r\n";
$headers .= 'Host: '.$hostname."\r\n";
$headers .= "Content-type: application/x-www-form-urlencoded\r\n";
$headers .= 'Content-Length: '.strlen($data)."\r\n\r\n";
// Отправляем HTTP-запрос серверу
fwrite($fp, $headers.$data);
// Получаем ответ
while ( !feof($fp) ) $content .= fgets($fp, 1024);
// Закрываем соединение
fclose($fp);
// Выводим ответ в браузер 
echo $content;
?>

Результат работы скрипта выглядит примерно так

HTTP/1.1 200 OK
Date: Sat, 28 Jun 2008 07:53:19 GMT
Server: Apache/2.0.55 (Win32) PHP/5.2.1
X-Powered-By: PHP/5.2.1
Content-Length: 42
Content-Type: text/html

Имя - Евгений<br/>
Пароль - qwerty<br/>

Загрузка POST-данных с использованием CURL

Помимо сокетов, обеспечивающих низкоуровневое обращение к серверу, PHP располагает специальным расширением CURL (Client URL Library).

<?php
// Задаем адрес удаленного сервера
$curl = curl_init("http://localhost/handler/action.php");
// Передача данных осуществляется методом POST
curl_setopt($curl, CURLOPT_POST, 1);
// Задаем POST-данные
$data = 'name=Евгений&password=qwerty';
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
// Выполняем запрос и выводим ответ в браузер
curl_exec($curl);
// Закрываем CURL соединение
curl_close($curl);
?>

С помощью функции curl_init() задается адрес удаленного сервера и путь к файлу на нем. В отличие от функции fsockopen(), необходимо задавать адрес полностью, включая префикс http://, т.е. расширение CURL позволяет работать с несколькими видами протоколов (HTTP, HTTPS, FTP). Если соединение с указанным сервером происходит успешно, функция curl_init() возвращает дескриптор соединения, который используется во всех остальных функциях библиотеки.

Для того, чтобы сообщить CURL о том, что данные будут передаваться методом POST, необходимо задать параметр CURLOPT_POST. POST-данные устанавливаются при помощи параметра CURLOPT_POSTFIELDS.

По умолчанию библиотека удаляет HTTP-заголовки, возвращаемые сервером. Однако CURL можно настроить на выдачу заголовков, если установить при помощи функции curl_setopt() ненулевое значение параметра CURLOPT_HEADER.

P.S. Из-за ошибки библиотеки сокетов протокол HTTP 1.1 под Windows работает медленно. При работе скрипта использующего сокеты под управлением этой ОС, лучше использовать версию HTTP 1.0.

Ссылки по теме: