PHP и Java Script: безопасные формы


FreeCat

Часть 1. Проверка данных формы при помощи JavaScript

разработка безопасных форм на php и javascriptКонечно, проверка данных, введенных пользователем в форму и переданных PHP-скрипту безусловно должна иметь место непосредственно в самом PHP-скрипте, дабы пользователь по ошибке не ввел некорректные данные, либо не сделал этого намеренно для провокации ошибки исполнения скрипта. Но, давайте представим, что у нас сайт с очень высокой посещаемостью, допустим, 10000 визитов в сутки и каждый пользователь вводит данные в форму и отправляет ее на сервер. Допустим, 50% пользователей не заполняют часть полей или заполняют их некорректно. Данные передаются скрипту, проверяются, и форма возвращается пользователю для исправления ошибок. В нашем случае, при большом количестве пользователей это создаст лишнюю нагрузку на сервер и лишний трафик как для сервера, так и для пользователей.

Чтобы избежать указанных выше проблем - целесообразнее использовать Java Script для проверки корректности заполнения полей формы. По статистике более 95% пользователей для серфинга по Интернету пользуются браузерами, поддерживающими Java Script. Пользователей, обладающих старыми версиями браузеров, либо браузерами не поддерживающими Java Script, либо сознательно отключивших Java Script по статистике менее 5%.

Итак, возьмем для примера следующую форму (обязательными для заполнения мы определим имя пользователя (sender) и текст сообщения (msg), т.к. E-mail либо ICQ у пользователя может попросту не быть):

<form name='myform' method='post' action='test.php'>
<input type='text' name='sender' value='' maxlength=20>
<input type='text' name='email' value=''>
<input type='text' name='icq' value='' maxlength=9 >
<textarea name='msg' cols=35 rows=10></textarea>
<input type='submit' name='Submit' value='Послать'>
</form>

Теперь будем определять правила, по которым будут проверяться отдельные поля.

Имя отправителя (sender) - не пустое, не короче, допустим, 3-х символов, не длиннее 20.

p_sender = document.myform.sender.value.toString();
if(p_sender != "") {
if(p_sender.length<3 || p_sender.length>20) {
alert ("Укажите ваше имя (3-20 символов)!");
document.myform.sender.focus();
return false;
}
} else {
alert("Необходимо ввести имя!");
document.myform.sender.focus();
return false;
}

Электронная почта (e-mail) - если указано, то проверяем корректность

p_email = document.myform.email.value.toString();
if (p_email != "") {
t = p_email.indexOf("@");
if((p_email.indexOf(".") == -1) || (t == -1) || (t < 1) || (t > p_email.length - 5) || (p_email.charAt(t - 1) == '.') || (p_email.charAt(t + 1) == '.')) {
alert("Некорректно указан E-mail !");
document.myform.email.focus();
return false;
}
}

Номер аськи (icq) - если указан, то состоит только из цифр, не короче 5 и не длиннее 9 знаков. Разместим часть ява-скрипта прямо в теге поля для ввода номера ICQ - такое ограничение позволит пользователю вводить в это поле только цифры

<input type='text' name='icq' value='' maxlength=9 onKeyPress ='if ((event.keyCode < 48) || (event.keyCode > 57)) event.returnValue = false;'>

Далее необходимо проверить длину введенного номера

p_icq = document.myform.icq.value.toString();
if(p_icq != "") {
if(p_icq.length<5 || p_icq.length>9) {
alert ("Длина номера ICQ 5-9 символов!");
document.myform.icq.focus();
return false;
}
}

Текст сообщения (msg) - не пустое и, допустим, не менее 10 символов

p_msg = document.myform.msg.value.toString();
if (p_msg.length < 10) {
alert ("Необходимо ввести текст сообщения (не менее 10 символов) !");
document.myform.msg.focus();
return false;
}

Еще один способ "защиты" форм - это "отключение" кнопки submit и включение ее только тогда, когда будут заполнены необходимые поля (работает не во всех браузерах, только поддерживающих JavaScript 1.2). "Отключается" кнопка просто. Для этого достаточно указать:

<input type='submit' name='Submit' value='Послать' disabled>

Теперь к полям обязательным для заполнения добавляем:

onkeypress='checkreq()' onkeyup='checkreq()' onblur='checkreq()'

и получаем

<input type='text' name='sender' value='' maxlength=20 onkeypress='checkreq()' onkeyup='checkreq()' onblur='checkreq()'>
<textarea name=msg cols=35 rows=10 onkeypress='checkreq()' onkeyup='checkreq()' onblur='checkreq()'></textarea>

Теперь определим функцию checkreq()

function checkreq() {
path = document.myform;
tmp = (path.sender.value == "");
if (!tmp && (path.sender.value.length < 3)) tmp = true;
path.Submit.disabled = tmp;
if (tmp) return;
tmp = (path.msg.value == "");
if (!tmp && (path.msg.value.length < 10)) tmp = true;
path.Submit.disabled = tmp;
}

Теперь "соберем" отдельные блоки, предназначенные для проверки конкретных полей в одну функцию, а к форме добавим процедуру, которая будет вызывать эту функцию перед отправкой формы PHP-скрипту.

<script>
// Проверка введенного имени и сообщения и "включение" кнопки submit
function checkreq() {
path = document.myform;
tmp = (path.sender.value == "");
if (!tmp && (path.sender.value.length < 3)) tmp = true;
path.Submit.disabled = tmp;
if (tmp) return;
tmp = (path.msg.value == "");
if (!tmp && (path.msg.value.length < 10)) tmp = true;
path.Submit.disabled = tmp;
}
// Проверка корректности заполнения полей формы
function check() {
p_sender = document.myform.sender.value.toString();
if(p_sender != "") {
if(p_sender.length<3 || p_sender.length>20) {
alert ("Укажите ваше имя (3-20 символов)!");
document.myform.sender.focus();
}
} else {
alert("Необходимо ввести имя!");
document.myform.sender.focus();
}
p_email = document.myform.sender.value.toString();
if (p_email != "") {
t = p_email.indexOf("@");
if((p_email.indexOf(".") == -1) || (t == -1) || (t < 1) || (t > p_email.length - 5) || (p_email.charAt(t - 1) == '.') || (p_email.charAt(t + 1) == '.')) {
alert("Некорректно указан E-mail !");
document.myform.email.focus();
return false;
}
}
p_icq = document.myform.icq.value.toString();
if(p_icq.length<5 || p_icq.length>9) {
alert ("Длина номера ICQ 5-9 символов!");
document.myform.icq.focus();
}
p_msg = document.myform.msg.value.toString();
if (p_msg.length < 10) {
alert ("Необходимо ввести текст сообщения (не менее 10 символов) !");
document.myform.msg.focus();
return false;
}
}
</script>

<form name='myform' method='post' action='test.php' onSubmit='return check();'>
<input type='text' name='sender' value='' maxlength=20 onkeypress='checkreq()' onkeyup='checkreq()' onblur='checkreq()'>
<input type='text' name='email' value=''>
<input type='text' name='icq' value='' maxlength=9 onKeyPress ='if ((event.keyCode < 48) || (event.keyCode > 57)) event.returnValue = false;'>
<textarea name=msg cols=35 rows=10 onkeypress='checkreq()' onkeyup='checkreq()' onblur='checkreq()'></textarea>
<input type='submit' name='Submit' value='Послать'>
</form>

Теперь при попытке пользователя отправить форму PHP-скрипту, управление передается сначала ява-скрипту, который возвращает FALSE если будет найдена хоть одна ошибка пользователя, курсор будет помещен в поле, содержащее ошибку (первое по счету), и форма просто не будет отправлена.

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

Данный пример, конечно, не охватывает всех возможных вариантов проверки полей формы (например, url или сравнение идентичности пароля и его подтверждения), т.к. может возникнуть необходимость проверять различные данные, вводимые пользователем. Здесь показаны наиболее часто встречающиеся поля и примеры их обработки. Остальное вы можете написать сами, конечно, если довольно не плохо знаете JavaScript.

 

Часть 2. Защита форм средствами PHP

До сих пор мы защищали PHP-скрипт от некорректных данных, переданных из формы. Но так же можно применить и средства, предлагаемые PHP, для защиты форм.

Наверняка многим знакома проблема флуда (для этого обычно используется простая программа, зачастую написанная на том же PHP). Думаю, вам будет не очень приятно обнаружить в своей гостевой книге пару тысяч сообщений, зачастую оскорбительного характера? Самый простой способ решения этой проблемы - небольшая картинка с кодом и поле для ввода этого кода (как на крупных почтовых сервисах). Думаете это очень сложно? Наоборот - это дело 5 минут. Итак, приступим.

Листинг файла code.php, генерирующего изображение со случайным чисом

<?php

session_start();
$сode = rand(100000,999999);
session_register("сode");
$Image = imageCreateFromPng ("code.png");
$Color = imageColorAllocate($Image, 254, 165, 65);
settype ($сode, "string");
imageString($Image, 3, 30, 3, $сode, $Color);
Header("Content-type: image/png");
imagePng($Image);
imageDestroy($Image);

?>

В файле code.png у нас храниться чистый (или с необходимым фоном) PNG файл размерами 100 на 20 пикселей. Случайное шестизначное число записывается в переменную $code, сохраняется в сессии, и накладывается на картинку. Данную картинку можно вывести в браузер следующим образом

<img src="code.php" width="100" height="20" alt="Код" border="0">

далее в форму добавляем поле, в которое пользователь введет код

<input type=text name=code2 value="">

И далее, после отправки пользователем формы PHP-скрипту нам останется сравнить код хранящийся, в сессии ($HTTP_SESSION_VARS['code']), с кодом, переданным пользователем ($HTTP_POST_VARS['code2']) и в случае несовпадения не принимать данные формы и вернуть ее пользователю. Таким образом, программа-флудер просто не сможет считать код с картинки.

Так же хотелось бы обратить внимание на момент, не указанный в статье Алатарцева Сергея, на которую я ссылался в начале. Как мы видим из примера нашей формы, данные PHP-скрипту передаются методом POST. Обратите внимание на предыдущий абзац: переменная code2 могла бы быть доступна в PHP-скрипте под именем $code2, но тогда ее можно было бы передать и методом GET, просто набрав в адресной строке браузера test.php?code2=123456. Чтобы лишить пользователя этой возможности необходимо получать переменные из массива $HTTP_POST_VARS, предварительно проверяя, была ли эта переменная передана методом POST - if(!IsSet($HTTP_POST_VARS['code2'])) {действие_если_переменная_не_существует}. Этот момент касается абсолютно всех переменных, передаваемых скрипту методом POST от формы.

Вот собственно и все. Теперь ваша форма будет более надежной.

04.08.04