ТаблицейБлоками.

Приложение Д: javascript html форма кнопки select php страница

Последняя модификация: 10.08.2014 г

Страница загружена с адреса: http://webdesign.site3k.ru/conjuncture/append/d/forms.html

Моя студия веб-дизайна

Формы (form), как они есть, или, по крайней мере, должны быть.

  1. Методы форм (form)
  2. Способы устранения недостатков методов POST и GET
  3. Запрет повторной отправки данных
  4. Типы содержимого формы с методом POST
  5. Контроль корректности введеных данных
  6. Формы и безопасность вашего сайта
  7. Проблемы со скриптами
  8. Заключение

В документации по HTML говорится о том, как создать формы, но толком не объясняется, каково их применение. В документации по JavaScript говорится о том, как проверить содержимое формы на корректность данных и о том, как их можно применять на стороне клиента. Но все эти описания и примеры подходят только для создания калькуляторов на веб-страницах. Реальное применение, формы находят только на стороне сервера при обработке данных скриптами на языках PHP и Perl, но в документации по ним, обработка форм рассматривается только на примитивном уровне. Поэтому, я считаю уместным, рассказать немного о формах: для чего и как их применять и, как это делать правильно.

Методы форм (form).

Первое, что хочется заметить по поводу форм – это применяемые в них методы, POST и GET.

Метод GET

Метод GET – это простой запрос файла, каким пользуется браузер, когда вы набираете адрес в адресной строке или щелкаете по ссылке. При использовании этого метода, браузер добавляет данные формы, оформленные как ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ и разделенные знаком «&», непосредственно к имени файла, указанного в атрибуте ACTION, после знака «?». Таким способом может работать любая, соответствующим образом оформленная, ссылка, например:

<A href=”http://myhost.ru/file.html?param1=number&param2=12345”>Тестовая ссылка с параметрами</a>

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

Недостатков у метода три:

  1. Кэширование данных, запрашиваемых данным методом серверами и браузерами. Зачастую это только ускоряет процесс загрузки, но иногда может привести к неверному отображению данных. Например, если в справочнике телефонов, вы запрашиваете телефоны всех людей по фамилии «Иванов», то, поскольку результирующий список не меняется каждую секунду, выдача списка из кеша ускорит отображение страницы. Если же методом GET проводить, например, опрос мнения, то…

Представим себе форму

Вы любите пиво?Да Нет

А сразу под формой выводятся результаты: сколько человек проголосовало, сколько любят пиво, а сколько, нет.

Когда человек делает свой выбор и нажимает на кнопку «Голосовать», браузер делает запрос типа http://myhost.ru/file.html?otv=1&do=голосовать (только русский текст URL-кодируется) и получает страницу, обработанную файлом http://myhost.ru/file.html в соответствии с заданными параметрами. Однако, в следствии кеширования, он, наверняка получит не результаты голосования данного посетителя, а результаты, устаревшие, как минимум на час и сформированные час назад, при голосовании кого-то другого, но с такими же предпочтениями (с той же строкой запроса). Более того, браузер может и не послать запрос в сеть, а достать страницу с соответствующим адресом и параметрами запроса из собственного кеша, если кто-то уже голосовал с этого компьютера ренее. В результате, человек не увидит результатов учитывающих его голос. Более того, при особо-неудачном написании скрипта, обрабатывающего запрос, благодаря кешированию, голосование вообще может работать лишь иногда, так как посетитель будет работать с кешем.

  1. Ограничение длины запроса 256 символами является вторым недостатком метода GET. С учетом того что, в эту строку запроса должен вместиться еще и путь к запрашиваемому файлу, начиная с «http://», на передачу параметров остается не много места и, его может не хватать для больших форм. На передачу файлов, места не хватит в любом случае.
  2. Отображение запроса в адресной строке так же можно отнести к недостаткам метода. Не все люди понимают, что за чехорда происходит в адресной строке и могут неправильно на нее реагировать.

Метод POST

Метод POST – этот метод, вместе с запросом файла, указанного в атрибуте ACTION, посылает дополнительный запрос, не отображаемый в адресной строке. Данные, полученные этим методом не кешируются, а длина запроса неограничена, вследствии чего появляется возможность отправлять с данными формы целые файлы. Все это можно считать основными достоинствами метода. Есть у него и 2 недостатка:

  1. Данным методом можно обращаться только к CGI или PHP-скриптам. Обычно такие файлы имеют расширения «.php3», «.php», «.cgi», «.pl» и «.py» и не индексируются поисковыми роботами. Исключение страниц из поисковых индексов может быть недопустимым.
  2. Если на странице с формой, использующей данный метод посетитель нажал на кнопку «Обновить», браузер сообщит о невозможности обновить страницу без повторной отправки данных и спросит разрешения отправить данные повторно. Если посетитель подтвердит повторную отправку данных, эффект может быть изряда вон выходящим. Например, на странице регистрации пользователя повторная отправка данных, скорее всего, приведет не к выдачи текста вроде «Спасибо за регистрацию», а к выдачи сообщения что такой пользователь уже существует (он был зарегистрирован при первой отправке формы). В результате, посетитель, не понимая, что зарегистрировался, может попытаться зарегистрироваться под другим именем (и не дай бог ему придет в голову еще раз нажать на кнопку «Обновить»). В итоге, база зарегистрированных пользователей будет содержать большее количество пользоватлей, чем их окажется на самом деле, что врядли соответствует пожеланию владельца регистрационной формы. Посетителю такое поведение тоже не удобно – ведь данные приходится вводить по нескольку раз. Если же, на запрос о повторной отправке ответить отрицательно, браузер, вместо требуемой страницы, выдаст бланк с надписью что, страница устарела и к ней нет доступа, а так же, предложит нажать на кнопку обновить – и тогда ситуация повторится.

В связи со своими особенностями, один метод лучше применять в одних ситуациях, другой в других. Однако, даже тогда, когда метод является наиболее подходящим к ситуации, недостатки метода могут привести к невозможности его применения, если не позаботиться об их устранении.

Способы устранения недостатков методов POST и GET

Способы устранения недостатков методов POST и GET

Кэширование и метод GET

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

Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Last-Modified: Mon, 26 Jul 2100 05:00:00 GMT

Эти заголовки могут быть установлены либо в мета-тегах http-equiv (Mon, 26 Jul 1997 05:00:00 предложено как один из вариантов даты в прошлом, а Mon, 26 Jul 2100 05:00:00 GMT, как вариант даты в будующем, вместо последнего можно применить SSI-инструкцию <!--#flastmod file="${DOCUMENT_NAME}" --> или <!--#echo var="DATE_GMT" -->):

<META http-equiv="Expires" content="Mon, 26 Jul 1997 05:00:00 GMT">
<META http-equiv="Cache-Control" content="no-cache, must-revalidate">
<META http-equiv="Pragma" content="no-cache">
<META http-equiv="Last-Modified" content="Mon, 26 Jul 2100 05:00:00 GMT">

Либо с помощью соответствующих функций языка, на котором написан сценарий. Например, на языке PHP, это выглядит так:

Header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
Header("Cache-Control: no-cache, must-revalidate");
Header("Pragma: no-cache");
Header("Last-Modified: Mon, 26 Jul 2100 05:00:00 GMT");

Где вместо жесткого задания даты можно применить gmdate("D, d M Y H:i:s", getlastmod()) или gmdate("D, d M Y H:i:s").

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

<input type=hidden name="id" value=<?php=md5(date("Ymdgis").mt_rand())?>>

Здесь md5 применен для укорочения полученного значения.

Ограничение длины GET-запроса

С ограничением длины GET-запроса бороться труднее. Конечно, стандарты постоянно меняются и некоторые программы имеют отступление от стандарта, так что, ваш браузер на вашем сайте, может быть, будет корректно работать с запросами превышающими 256 символов, но всегда следует учитывать что, у другого посетителя, такой фокус может не получиться и его браузер потеряет часть запроса. Поэтому, метод GET следует применять только на формах с небольшими объемами данных и стараться название полей (передаваемых переменных) и их значения делать покороче.

Отображение GET-запроса

Отображение GET-запроса будет подавлено, если обработав данные, скрипт переадресует стрницу на страницу без дополнительных параметров, в том числе, на самого себя. На PHP это выглядит так:

Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"]);1

Предупреждение о возможном нарушении конфиденциальности при отправке данных методом POST или GET

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

  1. Надеяться что посетителя вашей страницы не смутит сообщение о том что, отправленные им данные могут стать доступны другим ползователям интернета или что, выдача этого сообщения у него вообще отключена. Данный вариант подходит для опытных пользователей. У новичков выдача предупреждения обычно включена и, как и любое другое предупреждение, вызывает настороженность: «А стоит ли подтверждать отправку данных? Мало ли что отправится вмнсте с ними?». Но, конечно же, это настораживает лиш тех, кто сталкивается с формами впервые и, может быть проигнорировано.
  2. Снабдить форму текстом, достаточно вразумительно объясняющим, зачем владельцу сайта нужны запрашиваемые данные и, как он их соберается употребить. И не важно, насколько данное разъяснение будет соответствовать истине. Главное, добится у посетителя доверия к форме (надеюсь вы не пытаетесь выудить у него пароли доступа к банковским счетам?). Имея доверительное отношение, даже новичек со спокойной душой отправит данные в сеть, ни смотря ни на какие предупреждения браузера.

Обращение методом POST только к CGI или PHP-скриптам и индексирование страниц, к которым необходимо обращаться методом POST

Для того чтобы страницы, к которым обращаются методом POST, все же индексировались, можно зарегистрировать одно из индексируемых расширений (например, HTML) как cgi-script или application/x-httpd-php и писать CGI или PHP-скрипты в файлах с этим расширением. В этом случае, с одной стороны, HTML-файл будет поддерживать обращение методом POST, с другой стороны, не подозревая о том, что это, на самом деле, динамическая страница скрипта, поисковые машины будут обрабатывать его как статический HTML-документ. Страницам не содержащим скриптов тогда лучше давать другое расширение, чтобы не загружать сервер анализом скрипта, там, где его нет.

Другим – как я считаю, более правильным – способом, является включение скриптового файла в обычную страницу SSI-инстрцкцией #include virtual. Тогда обращение в форме, будет адресовано именно скриптовому файлу (например, <form method=POST action=<?php echo $_SERVER["SCRIPT_NAME"];?>>), а внутри этого файла можно установить заголовок, перебрасывающий браузер на страницу, в которую файл включен SSI-инстрцкцией. Например:

Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"].”.html”);

Устранение нежелательных последствий нажатия кнопки «Обновить»

Повторный ввод данных по нажатию кнопки «Обновить» происходит независимо от используемого формой метода. При использовании метода POST браузер предупредит его о необходимости повторной отправки данных, но где гарантия что посетитель окажется достаточно квалифицированным, чтобы понять, что сие означает и сделать правильный выбор? При использовании метода GET подобного предупреждения вообще не будет, данные молча отправятся в сеть, столько раз занося лишние записи в вашу базу данных, сколько раз посетителю захочется обновить страницу, ведь посетитель страницы, обычно, не имеет понятия о том что, нажимая кнопку «Обновить», он инициирует какие-то события на сайте. Можно утверждать, что это все из-за неправильных браузеров, что они не должны были бы отправлять данные формы при обновлении страницы, но, представьте себе ситуацию: Страница должна отображать часто изменяющуюся статистику в какой-то одной из возможных областей, например, текущее количество посетителей форума, учавствующих в обсуждении одной из его тем. В такой ситуации, браузер посылает запрос, содержащий эту тему и получает страницу, в которой указано текущее количество. Если при нажатии кнопки «Обновить», браузер не повторит запрос, вместо количества участников по данной теме, которое могло изменится, на странице отобразится общее количество участников форума, не только по данной, но и по другим темам, а может и, вообще, страница не отобразит статистики (в зависимости от того, как построен скрипт). То есть, браузер поступает правильно, посылая повторный запрос. Если же ваш скрипт не должен на него реагировать, значит именно в скрипте и должен быть установлен контроль за кнопкой «Обновить».

Самым простым способом отключить реакцию на нажатие кнопки «Обновить», является использование метода POST и разнесение формы и скрипта на разные страницы: Форма, использующая метод POST отображается в одном файле и обращается к скрипту в другом файле, который затем, выводится браузером без всяких форм. Поскольку формы на этой странице нет, обновление страницы не вызовет никакой отправки. Кроме того, поскольку метод POST не модифицырует адрес страницы (в отличие от метода GET, приделывающего к нему «хвост»), переменные, определяемые формой будут пусты, если вместо вызова из формы, страница будет запрошена на обновление. Однако этот способ имеет существенный недостаток: форма, отправляющая запрос и страница, отображающая ответ должны находится в разных файлах, а это не всегда удобно.

Для того чтобы, форма и ответ отображались на одной и той же странице, но нажатие кнопки «Обновить» не дублировало записи в базе данных, необходимо:

  1. До вывода какого-либо текста в браузер, программа должна проверить, нажата ли кнопка отправки данных, если да то, проверить данные и, если они не удовлетворяют условиям, выдать сообщение о их некорректности.
  2. В случае корректности данных, программа должна внести их в базу и переадресовать запрос на саму себя, установив заголовок
    Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"]);
    
  3. При повторном запросе, после переадресации, форма будет пустой и при обновлении отвергнута по той причине что, кнопка отправки данных не нажата. Отднако, именно при не нажатой кнопке отправки данных, программа считывает данные из базы и выводит в браузер.

То есть, код на PHP должен выглядеть примерно так:

if (@$DoGo){
   if(@$data != НЕДОПУСТИМОЕ_УСЛОВИЕ) {
      exit (‘Неверные данные’);
   }else{
      ЗАПИСЬ В БАЗУ
      Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"]);
      exit();
   }
}else{
   ЧТЕНИЕ ИЗ БАЗЫ И ВЫВОД НА ЭКРАН
}

Если требуется, чтобы, в зависимости от некоторых условий, вывод на экран был различным, строку переадресации можно модифицировать, добавив к ней строку параметров, а в блок чтения из базы и вывода значений добавить проверку этого параметра:

if (@$DoGo){
   if(@$data == НЕДОПУСТИМОЕ_УСЛОВИЕ) {
      exit (‘Неверные данные’);
   }else{
      ЗАПИСЬ В БАЗУ
Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"].’?param=1’);
exit();
   }
}else{
   ЧТЕНИЕ ИЗ БАЗЫ
   switch ($param) {
       case 0:
          0 ВЫВОД НА ЭКРАН
           break;
       case 1:
          1 ВЫВОД НА ЭКРАН
           break;
       case 2:
          2 ВЫВОД НА ЭКРАН
           break;
       default:
          ВЫВОД НА ЭКРАН ПО УМОЛЧАНИЮ
   }
}

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

<?php
if (@$data) {Header("Location: http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"]);}
// поскольку exit(); не стоит в этом месте, скрипт спокойно обрабатывает данные
?>
<html>
...

Запрет повторной отправки данных

Устранение негативных последствий нажатия кнопки «Обновить» не избавляет скрипт от элементарных недоразумений, вроде того что, долго ожидая ответа от сервера, посетитель думает: «А может я забыл нажать на кнопку?» и, на всякий случай, отправляет форму еще раз. Это знают все более-менее известные дизайнеры и нередко помещают возле кнопки отправки формы предупреждение «Не нажимайте повторно!». Но зачем? Ведь избавить скрипт от реакции на повторное нажатие кнопки «Отправить» еще легче, чем от нажатия на кнопку «Обновить». Конечно, иногда повторное нажатие должно обрабатываться так же, как и первое, но если вам требуется, чтобы отправка формы происходила только один раз, воспользуйтесь одним или несколькими из предложенных методов:

  1. Включить скрытое поле, которому присваевается значение другого поля, изначально пустого: если форма уже отправлялась и поле имело значение, при повторной отправке скрытое поле будет иметь значение и, обнаружив это, скрипт прекратит обработку или сообщит, что-то вроде «Да вы уже отправляли форму!».
  2. Обнулить обязательное поле. Обнаружив, что это поле пустое, скрипт прекратит обработку и вернет страницу, сообщив о недопустимости пустого поля.
  3. Убрать форму со страницы, чтобы посетителю больше нечего было нажимать.
  4. Обрабатывать данные формы и, при необходимости, отображать результат, другой страницей, не имеющей формы.
  5. После первого нажатия на кнопку отправки на кнопке можно изменить надпись с помощью JavaScript, например на "Ждите", или вообще скрыть ее под слоем с более высоким z-index.
  6. Если необходимо, чтобы посетитель не мог отправлять форму чаще чем 1 раз за все пребывание на сайте, можно, включить в нее скрытое поле, значение которого будет приращиватся счетчиком , если оно больше нужного числа (например, ноля, прекращать обрабатку запроса).
  7. В аналогичной ситуации, после первой отправки можно начать сессию, а если сессия уже начата (это будет обнаружено при повторной отправке), не обрабатывать данные.
  8. Если необходимо, чтобы посетитель не мог отправить форму раньше чем через какое-то время, после первой отправки, можно поставить ему куку на это время и, если кука установлена, не обрабатывать форму.

Типы содержимого формы с методом POST

Атрибут enctype элемента FORM с методом POST определяет тип содержимого, используемый при кодировке набора данных перед отправкой на сервер. Браузеры поддерживают, по крайней мере, 2 типа: application/x-www-form-urlencoded и multipart/form-data.

application/x-www-form-urlencoded

Это тип содержимого по умолчанию. Формы отправленные с содержимым этого типа, кодируются так:

Имена элементов управления, присвоенные им значения и зарезервированные символы переводятся в escape-последовательности, а символы пробелов заменяются плюсом. Неалфавитные символы заменяются «%HH» (знаком процента и двумя 16-ричными цифрами), представляющими ASCII-код символа. Символы новой строки представляются парами "CR LF" (напр., «%0D%0A»).

Пары имя/значение перечисляются в том порядке, в котором они появляются в документе. Имя отделено от значения знаком равенства (=), пары имя/значение отделены друг от друга знаком «&».

Тип кодирования содержимого очень близок по смыслу тому кодированию, которое происходит при отправке формы методом GET, с той только разницей, что метод GET не «унэскейпит» английские символы (только символы неанглийского алфавита) и не предназначен для отправки бинарных кодов.

multipart/form-data

Для отправки форм, содержащих файлы, не-ASCII данные и бинарные данные, следует использовать "multipart/form-data".

Сообщение "multipart/form-data" содержит несколько частей, каждая из которых представляет "действующий" элемент управления. Эти части пересылаются обработчику в том же порядке, в каком соответствующие элементы управления появляются в документе.

Как и все многочастные типы MIME, каждая часть может иметь заголовок "Content-Type", который по умолчанию содержит "text/plain".

Каждая часть содержит:

  1. заголовок "Content-Disposition", значением которого является "form-data".
  2. атрибут имя, определяющий имя соответствующего элемента управления.

Таким образом, для элемента управления с именем "mycontrol", соответствующая часть будет определена:

Content-Disposition: form-data; name="mycontrol"

Как и в других передачах MIME, "CR LF" (т.е., «%0D%0A») используется для разделения строк данных.

Каждая часть может кодироваться, и заголовок "Content-Transfer-Encoding" предоставляется, если значение этой части не соответствует кодировке по умолчанию.

Если содержимое файла отправляется с формой, ввод файла идентифицируется соответствующим типом содержимого (напр., "application/octet-stream"). Если несколько файлов возвращено как результат единого ввода формы, они возвращаются как "multipart/mixed", внедрённые в "multipart/form-data".

Браузер предоставляет имя для каждого пересылаемого файла. Имя файла может быть определено параметром "filename" в заголовке 'Content-Disposition: form-data' или, в случае с нескольким файлами, заголовком 'Content-Disposition: file' подраздела. Если имя файла клиентской операционной системы не-US-ASCII, имя файла может быть приближено или кодировано с использованием метода многоцелевых расширений Internet Mail. Это подходит для всех тех случаев, когда, например, подгруженные файлы могут содержать ссылку друг на друга.

Следующий пример иллюстрирует кодирование "multipart/form-data". Предположим, у нас есть такая форма:

<FORM action=http://server.com/cgi/handle enctype="multipart/form-data" method="post">
<P>Как Вас зовут? <INPUT type="text" name="submit-name"><BR>
Какие файлы Вы посылаете? <INPUT type="file" name="files"><BR>
<INPUT type="submit" value="Send"> <INPUT type="reset">
</FORM>

Если пользователь вводит "Larry" в окне ввода текста и выделяет текстовый файл "file1.txt", браузер посылает следующие данные:

Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
... содержимое файла file1.txt ...
--AaB03x--

Если пользователь выделит второй файл "file2.gif", браузер может создать части так:

Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=BbC04y
--BbC04y
Content-Disposition: file; filename="file1.txt"
Content-Type: text/plain
... содержимое файла file1.txt ...
--BbC04y
Content-Disposition: file; filename="file2.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary
...содержимое файла file2.gif...
--BbC04y--
--AaB03x—

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

 

Контроль корректности введеных данных

Контроль на стороне сервера

Поскольку не известно заранее, каким методом будут вводится данные (они могут быть набраны непосредственно в адресную строку), единственным безупречным способом проверки данных является их проверка на стороне сервера. Только после проверки корректности данных, они должы поступать на дальнейшую обработку. И, в первую очередь, следует удостоверится что данные введены (а может, потребуется проверка и доменна, с которого пришел запрос). Например, на языке PHP:

if (empty($var)) // если переменная пуста или не существует
if isset($var); // если переменная существует, независимо от того, есть ли у нее значение
if isset($_GET[$var]);  // если существует элемент массива, независимо от значения

Для проверки корректности данных следует учитывать порядок определения переменных. Для PHP,обычно, это EGPCS (Environment, GET, POST, Cookie, Server)2 . Различные переменные, установленые в этих областях видимости, но с одним и тем же именем, будут перекрывать друг друга слева на право. Переменная VAR введенная в адресную строку (GET-запрос) перебъет такую же переменную из окружения, затем сама будет перебита такой же переменной из метода POST и т. д. Таким образом, если переменной VAR методом GET задается значение 1, скрипт может получить значение «все что угодно», полученное, например, из кук. Если для скрипта важным является не только имя переменной, но и то, откуда она пришла, необходимо использовать следующие массивы:

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

$_SERVER – Переменные, установленные web-сервером или как-либо иначе относящиеся к среде окружения выполнения текущего скрипта. Аналогичен старому массиву $HTTP_SERVER_VARS (который ещё доступен, но не рекомендуется).

$_GET – Переменные, предоставляемые скрипту через HTTP GET. Аналогичен старому массиву $HTTP_GET_VARS (который ещё доступен, но не рекомендуется).

$_POST – Переменные, предоставляемые скрипту через HTTP POST. Аналогичен старому массиву $HTTP_POST_VARS (который ещё доступен, но не рекомендуется).

$_COOKIE – Переменные, предоставляемые скрипту через HTTP cookies. Аналогичен старому массиву $HTTP_COOKIE_VARS (который ещё доступен, но не рекомендуется).

$_FILES – Переменные, предоставляемые скрипту через HTTP post-загрузку файлов. Аналогичен старому массиву $HTTP_POST_FILES (который ещё доступен, но не рекомендуется).

$_ENV – Переменные, предоставляемые скрипту через среду окружения. Аналогичен старому массиву $HTTP_ENV_VARS (который ещё доступен, но не рекомендуется).

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

Примечание: Этот массив не имеет прямых аналогов в версиях PHP до 4.1.0.

$_SESSION – Переменные, зарегистрированные на данный момент в сессии скрипта. Аналогичен старому массиву $HTTP_SESSION_VARS (который ещё доступен, но не рекомендуется).

Например, для того, чтобы получить переменную VAR, определенную именно в методе GET, следует использовать $_GET[“VAR”].

Кроме определения существования и происхождения переменной, может потребоваться проверка типа, вхождения в диапазон и длины. Например, строковое значение может свести с ума программу, ожидающую цыфры, а превышение диапазона, создать лишний элемент массива. Самые курьезные случаи могут произойти, наверное, с превышением длинны строковых переменных. Например, в базе для имени посетителя отведено 30 символов. Посетитель ввел больше и, при записи, часть имени была отсечена. Когда посетитель входит следующий раз, вводя свое слишком длинное имя, ему сообщают, что такого в базе нет (его ввод дает ложное значение при сравнении с укороченным вариантом) и, ему ничего не остается, как регистрироваться еще раз. В результате, либо при повторном внесении укороченого имени выскакивает ошибка дублирования значения (которого, вроде бы, только что, небыло), либо база обрастает дублирующими записями, которые, как буд-то не сохраняются.

При использовании выпадающих списков с множественным выбором (HTML-элемент SELECT с атрибутом multiple), имя списка должно быть массивом (например name=”myoptions[]”). В этом случае, форма отправит скрипту массив выбранных значений, количество элементов которого можно определить функцией count() для последущей циклической обработки всех элементов массива.

Контроль на стороне клиента

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

Затруднения могут возникнуть при необходимости проверки радио-кнопок. Например, как определить, какая из них выбрана в форме с именем frm из группы кнопок по имени result?

<form name="frm" onsubmit="return test()" action=”script.php”>
<input type=radio name="result" value=1>Первая
<input type=radio name="result" value=2>Вторая
<input type=radio name="result" value=3>Третья
<input type=radio name="result" value=4>Четвертая
<input type=radio name="result" value=5>Пятая
<input type=submit name=golos value="Отправить">
</form>

Это можно сделать только таким скриптом:

<SCRIPT LANGUAGE="JavaScript"><!--
function test(){
   for (i=0, testvar=false; i < document.frm.result.length; i++){
      if (document.frm.result[i].checked){
         testvar=true;
         break;
      }
   }
   if (testvar==false){alert('Вы не сделали выбора!'); return false;} // ELSE переменная i содержит номер кнопки
}
//--></SCRIPT>

Формы и безопасность вашего сайта

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

Обеспечение безопасности ввода

Для обеспечения безопасности обработки данных посетителя, можно применять несколько PHP-команд:

escapeshellcmd(СТРОКА); Предваряет сомнительные символы в строке, которые могут привести к выполнению потенциально опасной команды экранирующим слешем, вследствии чего, эти символы перестают представлять опасность. Прежде всего, это касается кавычки с кодом 96 «`» (оператор выполнения). Представьте себе форму, отправляющую имя пользователя, вроде такой:

<FORM name="frm" ACTION="register.php">
<p>Укажите свое имя: <INPUT TYPE=text SIZE=35 MAXLENGTH=30 NAME="name">
<p><INPUT TYPE=SUBMIT name="doSend" VALUE="Отправить">
</FORM>

После того как посетитель ввел имя, скрипт должен выводить приветствие, вроде того:

<?php echo “Привет $name”;?>

Но если посетитель введет что-то типа `rm -rf *`, то вместо выведения текста «Привет `rm -rf *`», ваш скрипт удалит все содержимое Вашего сайта (этого не произойдет если в php.ini включен режим safe_mode или функция shell_exec() отключена директивой disable_functions в том же php.ini). Применение функции позволяет избежать подобных вторжений.

 

urldecode(СТРОКА); Декодирует любую %## кодировку в строке. Если скрипт принимает вместо нормальных букв URL-кодированную строку, данная функция вернет ей удобочитаемость.

 

HtmlSpecialChars(СТРОКА); Заменяет символы "<", ">", "&" и кавычки на их коды, так что, они отображаются на странице как сами символы, но не влияют на ее форматирование. Если в предыдущем примере посетитель введет «<!--» (начало комментария) весь дальнейший код страницы, после слова «Привет», будет подавлен. Избежать этого позволяет данная команда.

 

strtr(СТРОКА, ЗАМЕНЯЕМЫЕ_СИМВОЛЫ, ЗАМЕНИТЕЛИ); перечисляет несколько символов, которые следует заменить и (в той же последовательности), символы для замены. Например, для смены регистра: strtr(СТРОКА, "АБВ","абв"). Данную команду удобно применять для замены специально используемых вами символов (например, разделителей значений) на другие символы, если те оказались в пользовательском вводе. Не заменив их, вместо одного поля, вы получите несколько.

 

strip_tags(СТРОКА [, ПРОПУСКАЕМЫЕ_ТЕГИ]); Удаляет теги, оставляя пропускаемые. Например, удалить все теги, кроме обзаца (<P>) и новой линии (<BR>): strip_tags(СТРОКА,"<P><BR>"). В связи с добавлением новых тегов в новых версиях HTML, нет гарантии, что данная функция всегда сработает корректно, если, конечно, она не удаляет просто все то, что нахадится между «<» и «>», и сами эти символы впридачу (точного указания ее действия в документации нет). Оказавшиеся в пользовательском вводе и, затем вставленные в вашу страницу непрошенные теги могут до неузнаваемости исказить ее внешний вид, например, набор «</table></table></table></table>» исказит любую страницу, так как все нынешние страницы являют собой набор таблиц. Если эти таблицы закончатся преждевременно, то на страницах не отобразится их содержимое.

 

str_replace(ИСКОМОЕ, ЗАМЕНИТЕЛЬ, СТРОКА); Заменяет в строке все вхождения искомого, с учетом регистра, заменителем. Например str_replace("<","&lt",СТРОКА) заменит начало тега на его код, с тем, чтобы предотвратить попытку ввести тег в поле, которое затем отобразится на экране (и может испортить вид). Этот способ может заменить любой из вышеперечисленных, не считая escapeshellcmd.

 

ereg_replace(); Выполняет «интеллектуальную» замену. Например:

$str=ereg_Replace("(<([^>])*>|>|<)","",$str);

Удалит все теги (например <B>, </b>, <pre>? <p id=”exampl”>, </p>) в строке «$str», выискивая все, вплоть до бинарных значений, что находится внутри угловых скобок. Первая часть (<([^>])*>) показывает, что нужно искать все от левой скобки до правой, не считая саму правую скобку (иначе, оно не остановится на первой правой скобке и возмет для замены всю строку или файл – от <html> до </html>). Вторая часть (|>) показывает, что если где-то еще остались правые угловые скобки, их заменить тоже. Третья часть (|<) указывает на замену правых угловых скобок. Поскольку механизм функции strip_tags неизвестен и невозможно определить ее надежность, я рекомендую использовать ereg_Replace для удаления тегов, так как она безупречно работает даже с немыслемой смесью угловых скобок с другими символами и бинарными кодами, в которых strip_tags теряла весь текст. В целом, рекомендуемый ряд проверок выглядит так:

$str=@$str; // если переменная не определена, не кричать об ошибке

$str=ereg_Replace("(<([^>])*>|>|<)","",$str); //Вытащить текст из тегов

$str=str_replace("`",'&#096;',$str); //Заменить оператор выполнения (символ 96) кодом

$str=str_replace("'",'&#039;',$str); //Заменить одинарные кавычки их кодами

$str=str_replace('"','&quot;',$str); //Заменить двойные кавычки их кодами (теперь никакие кавычки не попадут в страницу)

Попробуйте в форме без такой защиты ввести текст `<!--<a href="">>"Здесь 'хулиганил' Вася!"<</a>`, или что-то еще напичканное подозрительными символами.

Однако проверка каждого поля может быть слишком громоздким занятием. Упростить дело можно используя команду array_walk:


function check(&$item, $key) {
	if (is_array($item)){
		array_walk($item, 'check');// пока не доберемся до строки
	}else{
		$item=str_replace("\`",'&#096;',$item);// заслешенный оператор выполнения (символ 96) заменить кодом
		if (get_magic_quotes_gpc()) {
			$item=stripslashes($item); // расслешить строку (теперь-то она безопасна)
		}
		$item=urldecode($item); // раскодтрование из 16-ти ричного вида, если переменная закодирована (актуально для GET-запросов)
		$item=str_replace("`",'&#096;',$item); //Заменить оператор выполнения (символ 96) кодом 
		$item=str_replace("'",'&#039;',$item); //Заменить кавычку кодом
		$item=str_replace('"','&#034;',$item); //Заменить кавычку кодом
		$item=str_replace("<",'&#060;',$item); //Заменить левую угловую скобку (символ 60) кодом
		$item=str_replace(">",'&#062;',$item); //Заменить правую угловую скобку (символ 62) кодом
		$item=trim($item); // обрезание начальных и конечных пробелов (полезно, если ввод должен сравниваться с чем-то) 
	}
}

array_walk($_GET, 'check');// обработать все данные $_GET-запроса функцией check

Дополнительно к перечисленным, могут пригодиться такие команды:

 

addslashes(СТРОКА); «Заковычивает» строку, подобно htmlspecialchars, но с учетом особенностей баз данных. Ей следует преобразовывать данные перед сохранением в базу данных (возвратит данные в исходное состояние функция stripslashes).

 

convert_cyr_string(СТРОКА, ИСХОДНАЯ, НОВАЯ); Перекодирует кириллицу из одной кодировки в другую. Кодировка определяется буквой в соответствии с таблицей:

k — koi8-r

w — windows-1251

i — iso8859-5

a — x-cp866

d — x-cp866

m — x-mac-cyrillic

Например convert_cyr_string(СТРОКА, "k","w") перекодирует строку из koi8-r в windows-1251. Данная функция может потребоваться для перекодировки текста в читабельный вид (не вполне относится к безопасности, но очень увязывается с перечисленными функциями замен).

 

mysql_real_escape_string(СТРОКА); предотвращает SQL-иньекции. Рекомендуется обрабатывать ей данные перед использованием в SQL_запросах.

Натянутая безопасность md5 и безопасность паролей.

То, что данные зашифрованные методом md5 невозможно расшифровать (поскольку никакого ключа шифрования не существует, а зашифрованные данные еще и усекаются до 32 символов, так что, даже при наличии ключа, нельзя былобы однозначно утверждать, какой была строка до шифрования), не означает что, пароль к зашифрованной строке нельзя подобрать. Переберая случайные строки, шифруя их методом md5 и сравнивая с зашифрованой строкой пароля, можно, через какое-то время подобрать ту строку, которая при шифровании совпадет с парольной и, таким образом, подобрать пароль. Конечно, для этого нужно время. Но затраты времени могут не оказаться достаточно большими, чтобы считать пароль надежным, если злоумышленик получит базу, хранящую зашифрованные пароли и начнет ее штурм на своем компьютере. Поэтому, постарайтесь сделать так, чтобы файл паролей оказался недоступен из интернет. Закройте ему доступ через htaccess, дайте ему ни чем не выделяющееся имя и засуньте в дебри подкаталогов. Если же ваши ресурсы должны быть особо защищены, фиксируйте дату последней смены пароля и не пускайте посетителя никуда, кроме страницы смены пароля, если, через определенное время, недостаточное для подбора пароля хакером, посетитель не создаст себе новый пароль, чтобы свести все труды хакера на смарку.

Неаккуратная запись и потеря данных.

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

$f=fopen(“ФАЙЛ”,"a+") or die("Не могу открыть файл на запись!");
flock($f,LOCK_EX); // ждем, пока не станем единственными и блокируем
ftruncate($f,0); // очищаем все содержимое файла
fwrite($f, ДАННЫЕ); // записываем данные в файл
fflush($f); // записываем все изменения на диск
flock($f,LOCK_UN); // говорим что, больше не будем работать с файлом и разблокируем
fclose($f); // Закрываем файл

Для баз данных дребуется блокировка отдельных записей.

Проблемы со скриптами

На некоторых серверах переменные масивов GET и POST не становятся автоматически доступны скриптам. В этой ситуации можно использовать массив $_REQUEST, разбивая его на элементы, но проще использовать массивы $_POST и $_GET. Например, значение $_GET-переменной $menu, можно получить как элемент $_GET['menu'].

Заключение

В данной статье изложены общие принципы работы с формами, знание которых поможет лучшим образом огранизовать связь с посетителем сайта. Конкретные примеры форм, при желании, можно найти в тех страницах практического руководства, в которых рассматривается решение задачь, так или иначе связанных с формами. Например, на странице «Обработка баз данных сервером» формы применены для фильтрации содержимого базы данных и не нуждаются ни в запрете кеширования, ни в исключении повторной отправки или обновления. В голосовании, не имеет значение кэширование, так как свежие данные всегда берутся из базы на сервере и, в худшем случае, посетитель не увидет влияние собственного голоса. Зато повторная отправка данных при обновлении страницы исключена. В гостевой книге не допускается ни кэширование, ни повторная отправка при обновлении страницы (но разрешена отправка дополнительных замечаний в книгу). В процедуре регистрации, после успешной регистрации отправка данных исключается до следующего захода на сайт. Все это не только разные задачи, но и задачи с разными требованиями к реакции на повторный ввод. Поэтому каждая из них имеет не только собственные реакции на данное событие, но и собственную реализацию реакции при ее совпадении. А это означает что, даже если вам нет необходимости осваивать создание гостевой книги или голосования, вам все равно стоит взглянуть на их описание, чтобы чтобы обогатить свой опыт построения форм примененными в них приемами.

 


1 Если на вашем сервере PHP работает как CGI-скрипт, SCRIPT_NAME будет направлять браузер на каталог вроде cgi-bin и выдавать ошибку. В такой ситуации следует использовать Header("Location: ".$PHP_SELF);. Вместо PHP_SELF можно использовать любую другую подходящую переменную, получить значения которых позволяет включение в скрипт функции phpinfo().

2 Проверить установленный на сервере порядок, можно директивой get_cfg_var("variables_order");

 

Комментарии к странице (всего 1)

 

 

 


На главную страницу сайта