Пишем скрипт гостевой книги
http://zolotov.h14.ru/
В этой статье мы рассмотрим, как написать свою гостевую книгу. И так поставим цель: простейшую гостевую книгу на PHP с использованием базы данных MySQL. Наша гостевая не будет обременена красивым интерфейсом и защитой, главное, чтобы она работала. Приведенные здесь скрипты основаны на скриптах мой гостевой книги, но значительно упрощены для лучшего понимания принципа работы скриптов, а все остальное: форматирование текста, смайлы, скины и пр. можно легко добавить. Скачать готовые скрипты можно здесь.
Этап первый: планирование.
Для начала определимся с таблицей MySQL, в которой мы будем хранить сообщения нашей гостевой книги. Нам хватит одной таблицы. Основные параметры, которые должны быть в любой гостевой это имя, сообщение и дата, когда было оставлено сообщение. Но мы введем еще пару полей: адрес электронной почты, url сайта, и ICQ. Исходя из этого нам нужно составить SQL-запрос, создающий нужную нам таблицу. Он будет выглядеть так:
CREATE TABLE $table( k_msg int primary key auto_increment, guest varchar(20), email varchar(40), url varchar(60), icq varchar(15), time int, msg mediumtext, replay mediumtext
Что же мы сделали. Этот запрос создаст таблицу gbook
и в ней будут определены следующие поля:
k_msg
- уникальный номер сообщения, первичный ключ таблицы, он нам понабиться, когда мы будем работать над панелью модерирования нашей гостевой.guest
- имя гостя, 30 символов должно быть достаточно, но при желании можно и увеличить;email
- адрес электронной почты гостя;url
- URL-адрес сайта;icq
- аська гостя;time
- время, когда было оставлено сообщение;msg
- собственно сообщение. Типmediumtext
позволяет хранить текст размером до 64Кб;replay
- ответное сообщение от администратора.
Этап второй: скрипт установки
Рядовой пользователь может быть незнаком с PHP или MySQL, по этому мы не будем его обременять ручным созданием нужной нам таблицы и настройки нашей гостевой книги. Поэтому нам нужен инсталляционный скрипт который все сделает за него (и за нас тоже). Наш инсталляционный скрипт должен запросить у пользователя основные параметры у пользователя, подключиться к базе данных, создать таблицу и конфигурационный файл нашей гостевой книги.
install.php
<html> <head><title>Установка гостевой книги</title></head> <body> <h2>Установка гостевой книги</h2> <form action=<?=$SCRIPT_NAME?> method=post> <table> <tr><td align=right>Хост:</td> <td align=left><input type=text name=host value=<?=$host?>></td></tr> <tr><td align=right>Логин:</td> <td align=left><input type=text name=login value=<?=$login?>></td></tr> <tr><td align=right>Пароль:</td> <td aling=left><input type=password name=password></td></tr> <tr><td align=right>База:</td> <td align=left><input type=text name=database value=<?=$database?>></td></tr> <tr><td align=right>Таблица:</td> <td align=left><input type=text name=table value=<?=$table?>></td></tr> <tr><td align=right>Пароль админа:</td> <td align=left><input type=password name=pass></td></tr> </table> <input type=submit name=go value="Install"> </form> <? function error() // Выводим сообщение об ошибке и выходим { echo "Error #".mysql_errno().": ".mysql_error(); exit; } if(isset($go)):// Если пользователь передал параметры echo "Подключаемся к MySQL...<br>\n"; mysql_connect($host,$login,$password) or error(); echo "Выбираем базу данных $database...<br>\n"; mysql_select_db($database) or error(); echo "Удаляем таблицу (если существует) $table...<br>\n"; mysql_query("DROP TABLE IF EXISTS $table") or error(); echo "Создаем новую таблицу $table...<br>\n"; mysql_query(" CREATE TABLE $table( k_msg int primary key auto_increment, guest varchar(20), email varchar(40), url varchar(60), icq varchar(15), time int, msg mediumtext, replay mediumtext )") or error(); // Создадим файл конфигурации и запишем в него // параметры подключения и пароль модератора гостевой $f=fopen('gbook.conf','w'); flock($f,LOCK_EX); // Исключительная блокировка файла fputs($f,"$host\n$login\n$password\n$database\n$table\n$pass"); flock($f,LOCK_UN); // Снимаем блокировку с файла fclose($f); // Создадим файл .htaccess и укажем в нем // что по умолчанию нужно открывать файл gbook.php $f=fopen('.htaccess','w'); flock($f,LOCK_EX); fputs($f,"DirectoryIndex gbook.php\n"); flock($f,LOCK_UN); fclose($f); // Установим права на чтение/запись // только для владельца exec('chmod 600 gbook.conf'); exec('chmod 600 install.php'); // Все сделано можно перейти на готовую гостевую echo "<b>Инсталляция завершена</b><br>"; echo "<a href=gbook.php>Гостевая книга</a>"; endif; ?> </body> </html>
Замечание. После создания конфигурационного файла мы выставляем на него права для чтения и записи только для хозяина. Это сделано для того, чтобы злоумышленник не смог прочесть наш файл конфигурации: в нем мы храним параметры подключения к базе данных и пароль модератора гостевой книги. Данная защита будет работать только на Unix/Linux хостинге. Для Windows-хостинга нужна другая защита, но приведенные команды не должны порождать ошибок.
Этап третий: отображение гостевой книги
Здесь мы рассмотрим скрипт, отображающий нашу гостевую книгу. Мы в нескольких скриптах будем выполнять ряд одинаковых действий и, чтобы не повторяться вынесем их в один файл gbook.lib.
gbook.lib
<? function htmltrim($str) { // Просто для краткости return HtmlSpecialChars(trim($str)); } function error($msg) // Выведем сообщение об ошибке и остановим скрипт { echo "<b>Ошибка:</b> $msg"; exit; } function my_error() // Ошибка MySQL { echo "<b>Ошибка MySQL</b> #".mysql_errno().": ".mysql_error()."</b><br>"; exit; } function my_no_cache() // Отключить кэширование страницы { Header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); Header("Cache-Control: no-cache, must-revalidate"); Header("Pramga: no-cache"); Header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); } function my_redirect($url) // Включить перенаправление { echo '<html><head><meta http-equiv="refresh" content=1> <script language=JavaScript>location="'.$url.'"</script></head></html>'; } $MY_MONTH_RU=array('января','февраля','марта','апреля','мая', 'июня','июля','августа','сентября','октября','ноября','декабря'); function my_date($d) // Форматирование даты { global $MY_MONTH_RU; return date("j ",$d).$MY_MONTH_RU[date("m",$d)-1].date(" Y - H:i:s",$d); } function gbook_init() // Инициализация { // Объявим глобальные переменные global $table, $admin; // Прочитаем параметры подключения к БД $f=@fopen('gbook.conf','r') or error("<b>Нет файла конфигурации</b>"); flock($f,LOCK_SH); $host=trim(fgets($f)); $login=trim(fgets($f)); $password=trim(fgets($f)); $database=trim(fgets($f)); $table=trim(fgets($f)); $pass=trim(fgets($f)); flock($f,LOCK_UN); fclose($f); // Подключаемся к БД mysql_connect($host,$login,$password) or my_error(); mysql_select_db($database) or my_error(); // Проверим пароль if(isset($_GET['sid']) || isset($_POST['sid']) || isset($_COOKIE['sid'])) { if(isset($_POST['sid'])) $sid=$_POST['sid']; elseif(isset($_GET['sid'])) $sid=$_GET['sid']; else $sid=$_COOKIE['sid']; @SetCookie("sid",$sid,time()+60*15); $admin=($sid===md5($pass.date('dmYH'))); } } my_no_cache(); ?>
Итак собственно скрипт отображающий гостевую книгу:
gbook.php
<? require_once "gbook.lib"; gbook_init(); // Выберем все сообщения из таблицы // записи сортируем по убыванию времени: // от последних сообщений к первым $r=mysql_query("select k_msg, guest, email, url, icq, time, msg, replay from $table order by time desc") or my_error(); $pgsz=8; // Количество сообщений на страницу $pgbar=''; // Здесь будет сформирован текст ссылок на страницы if(!isset($pg))$pg=0; // По умолчанию с первого сообщения $rows=mysql_num_rows($r); // Число записей = число сообщений $pgcnt=$rows/$pgsz; // Число страниц // Формируем строку страниц for($i=0;$i<$rows;$i+=$pgsz) if( ($i>=$pg) && ($i<($pg+$pgsz)) ) $pgbar.=($i/$pgsz+1); else $pgbar.="<a href=gbook.php?pg=$i>".($i/$pgsz+1)."</a> "; ?> <html> <head> <title>Гостевая книга</title> <meta http-equiv=Content-Type content="text/html; charset=windows-1251"> </head> <body> <h2 align=center>Гостевая книга</h2> <div align=center><?=$pgbar?></div> <table width=100% border=1> <? // Выведем сообщения $i=0; while($f=mysql_fetch_array($r)) { if( ($i>=$pg) && ($i<($pg+$pgsz)) ) { echo "<tr><td align=center valign=top width=25%>"; echo htmltrim($f['guest'])."<br>"; $email=htmltrim($f['email']); $href='mailto:'.$email; echo "<a href=$href>$email</a><br>"; $url=htmltrim($f['url']); echo "<a href=$url>$url</a><br>"; echo "ICQ: ".htmltrim($f['icq'])."<br>"; echo my_date($f['time']); echo "</td>"; $mes=nl2br(htmltrim($f['msg'])); echo "<td align=left valign=top>$mes"; $replay=nl2br(htmltrim($f['replay'])); if($replay): echo "<hr>$replay"; endif; echo "</td>"; if($admin): // А это только для администратора echo "<td width=10% align=center valign=top>"; // Ссылка для редактирования сообщения echo "<a href=replay.php?sid=$sid&id=$f[k_msg]>Ответить</a><br>"; // Ссылка для удаления сообщения echo "<a href=drop.php?sid=$sid&id=$f[k_msg]>Удалить</a>"; echo "</td>"; endif; echo "</tr>"; } $i++; if($i>=($pg+$pgsz)) break; } ?> </table> <div align=center><?=$pgbar?></div> <table align=center><tr><td> <form action="post.php" name=frm method=post> <h3 align=center>Оставьте свое сообщение</h3> <table> <tr><td align=right><b><font color=red>*</font>Имя:</b></td> <td><input class=e type=text name=Login size=40></td></tr> <tr><td align=right><b>e-mail:</b></td> <td><input class=e type=text name=Email size=40></td></tr> <tr><td align=right><b>URL сайта:</b></td> <td><input class=e type=text name=Url size=40></td></tr> <tr><td align=right><b>ICQ:</b></td> <td><input class=e type=text name=Icq size=40></td></tr> </table> <b><font color=red>*</font>Сообщение:</b><br> <center> <textarea class=t cols=40 rows=8 name=Msg></textarea><br> <input type=submit value="Отправить"> </center> </form> <div align=center><br>gbook 1.0 beta © Золотов Алексей 2005<br> <a href=http://zolotov.h14.ru>http://zolotov.h14.ru</a> </div> </body> </html>
Вот и все. Это самое основное, осталось сделать панель модерирования гостевой книги, чтобы можно было легко удалять и редактировать оставленные сообщения. Для входа в режим администрирования создадим отдельный скрипт. Когда пользователь вводит пароль, то на основе его и текущей даты генерируется код сессии, если переданный в скрипт код сессии правильный, то считается, что зашел администратор, и ему открываются все возможности.
admin.php
<? $addr=dirname($SCRIPT_NAME); if($addr=='/')$addr='/gbook.php'; else $addr.='/gbook.php'; $addr="http://".$SERVER_NAME.$addr; require_once "gbook.lib"; if(isset($pass)) { my_redirect($addr."?sid=".md5($pass.date('dmYH'))); exit; } ?> <html> <head> <title>Гостевая книга</title> <meta http-equiv=Content-Type content="text/html; charset=windows-1251"> </head> <body> <h3 align=center>Вход в режим модерирования</h3> <form action=admin.php method=post> <table align=center><tr><td> <b>Введите пароль:</b> <input type=password name=pass> <input type=submit value="Войти"> </td></tr></table></form> <div align=center><br>gbook 1.0 beta © Золотов Алексей 2005<br> <a href=http://zolotov.h14.ru>http://zolotov.h14.ru</a></div> </body> </html>
В главном скрипте в режиме администратора мы создали ссылки для редактирования и удаления сообщений. Для начала приведу скрипт для удаления сообщения - он короче.
drop.php
<? require_once "gbook.lib"; $id=trim($id); gbook_init(); if(!$admin) error("Отказано в доступе"); if(!$id) error("Не задан идентификатор удаляемого сообщения<b>id</b>"); // Удалим сообщение mysql_query("delete from $table where k_msg=$id") or my_error(); $addr=dirname($SCRIPT_NAME); if($addr==='/') $addr.='gbook.php'; else $addr.='/gbook.php'; $addr="http://".$SERVER_NAME.$addr; my_redirect($addr); echo "<b>Сообщение удалено. <a href=$addr>Назад</a></b>" ?>
А теперь скприпт, отображающий страницу для редактирования сообщения.
replay.php
<? require_once "gbook.lib"; $id=trim($id); gbook_init(); if(!$admin) error("Отказано в доступе"); if(!$id) error("Не задан идентификатор редактируемого сообщения<b>id</b>"); $r=mysql_query("select k_msg, guest, email, url, icq, time, msg, replay from $table where k_msg=$id") or my_error(); if(mysql_num_rows($r)==0) error("Сообщения с id=$id нет в гостевой таблице"); $f=mysql_fetch_array($r); $name=htmltrim($f['guest']); $email=htmltrim($f['email']); $url=htmltrim($f['url']); $icq=htmltrim($f['icq']); $msg_n=htmltrim($f['k_msg']); $replay=htmltrim($f['replay']); $msg=htmltrim($f['msg']); $date=my_date($f['time']); ?> <html> <head> <title>Гостевая книга</title> <meta http-equiv=Content-Type content="text/html; charset=windows-1251"> </head> <body> <h3 align=center>Редактирование сообщения</h3> <form action=save.php method=post> <table align=center><tr><td> <table> <tr><th align=right>Имя:</th> <td><input class=e type=edit name=Login value='<?=$name?>'></td></tr> <tr><th align=right>E-Mail:</th> <td><input class=e type=edit name=Email value='<?=$email?>'></td></tr> <tr><th align=right>ICQ:</th><td> <input class=e type=edit name=Icq value='<?=$icq?>'></td></tr> <tr><th align=right>Сайт:</th><td> <input class=e type=edit name=Url value='<?=$url?>'></td></tr> <tr><th align=right>Дата:</th><td><?=$date?></td></tr> </table> </td><td> <b>Сообщение:</b><br> <textarea cols=40 rows=10 name=Msg><?=$msg?></textarea><br> <b>Ответ:</b><br> <textarea cols=40 rows=10 name=Replay><?=$replay?></textarea> </td></tr></table> <input type=hidden name=sid value=<?=$sid?>> <input type=hidden name=id value=<?=$msg_n?>> <center><input type=submit value='Сохранить'></center> </from> <div align=center><br>gbook 1.0 beta © Золотов Алексей 2005<br> <a href=http://zolotov.h14.ru>http://zolotov.h14.ru</a></div> </body></html>
Сохранение редактированного сообщения происходит в скрипте save.php
save.php
<? require_once "gbook.lib"; $Login=trim($Login); $Email=trim($Email); $Url=trim($Url); $Icq=trim($Icq); $Msg=trim($Msg); $Replay=trim($Replay); $k_msg=trim($id); gbook_init(); if(!$admin) error("Отказано в доступе"); if(!$k_msg) error("Неуказан идентификатор <b>id</b> сохраняемого сообщения"); if(!$Login || !$Msg) error("Поля <b>Имя</b> и <b>Сообщение</b> должны быть заданы"); // Обновим запись mysql_query("update $table set guest='$Login', email='$Email', url='$Url', icq='$Icq', msg='$Msg', replay='$Replay' where k_msg=$k_msg") or my_error(); $addr=dirname($SCRIPT_NAME); if($addr==='/') $addr.='gbook.php'; else $addr.='/gbook.php'; $addr="http://".$SERVER_NAME.$addr; my_redirect($addr); echo "<b>Сообщение отправлено. <a href=$addr>Назад</a></b>" ?>
Вот и весь скрипт. Осталось только установить и использовать.
Оставить комментарий
Комментарии
<form action="post.php" name=frm method=post>
Если можно, опишите поподробней данный момент.
Спасибо
Но более надежный вариант вы найдете на моем сайте http://zolotov.h14.ru/doc/php/gbook.php и там же прочесть про установку.
Но есть один нюанс (глюк, так сказать) в текущей версии gbook 1.0: после установки нужно зайти в админпанель гостевой и установить максимальный размер сообщения например в 4096 , а то пользователи не смогут оставлять сообщения.
2. Лучше, но совсем не обязательно. Порядочный гость оставит сообщение так как нужно (через мою форму), а хороший хакер все равно обойдет ваши $_GET, $_POST, $_COOKIE и $HTTP_REFERER тоже.
3. Контроль длины производиться, но только неявно, самой БД (единственно, что только само сообщение может быть огромно - до 64Кб).
4. Да, есть, HtmlSpecialChars была использована, не использовал AddSlashes (и это большая ошибка, признаю свою вину, см. ниже). При в ключеной magic_quotes_gpc, данная проблема не столь остра, но дыра в безопасности остается (в панели управления).
5. Да, согласен, можно было бы и вырезать, но имя #$@%#$^%$ ничем не хуже чем AF4ETX09T43. В e-mail и url есть дырка, можно использовать скрипты.
6. Интересно, что не неинициализировано?
...
Картинок кажется небыло, зачем усложнять демонстрационный пример. Пока ни разу не встречал гостевой с подобной защитой. Что касается этого сайта, то это не гостевая.
Анатомия межсайтового скриптинга XSS
http://www.woweb.ru/index.htm/id/1073393942
Очень интересно, спасибо.
З.Ы. Если бы Аффтор потрудился бы почитать (и вниктуть) в статьи что на этом же сайте, то понял бы, на сколько его труд непрофессионален. Стоит учитывать опыт предыдущих Авторов и, по крайней мере, уважать их труды - они же для вас писали.
Где есть не уважение? Извините если кого обидел.
Что касается защиты, то советую еще раз прочесть первый абзац статьи, я не ставил цели расматривать надежную гостевую, а лишь показать как можно написать протейшую гостевую, для тех кто только начинает познавать CGI, ведь не все сразу приходит, надо начинать с простого, и ты тоже не сразу стал таким умным, тоже совершал ошибки, так что давай оставим аспекты защиты другим статьям, другим авторам.
Да, с точки зрения защиты этот скрипт непрофессионален, и я непрофесионал в области защиты, поэтому в первом абзаце и стоит соответвующая оговорка, которую, к сожалению не все прочли.
PS <blockquote><small>Цитата:<hr size=1>
Закон "Об авторском праве и смежных правах"
Статья 6. Объект авторского права. Общие положения
1. Авторское право распространяется на произведения науки, литературы и искусства, являющиеся результатом творческой деятельности, независимо от назначения и достоинства произведения, а также от способа его выражения.
[/quote]
Остальное можешь прочесть тут: http://www.febras.ru/~patent/copyright/2_3part2.html
В том числе и Статья 9. п.1
И не тебе решать пользоваться мне моим правом или нет.
Если бы не это битье пяткой в грудь (типа вот я весь такой Д'Артаньян а вы все...)
то не было бы такой критики.
http://www.codenet.ru/webmast/php/security.php
Могу добавить (и обобщить):
1. выключать register_global (основной источник заразы)
2. к параметрам лучше всего обращаться через специально для этого предназначенные массивы $_GET, $_POST, $_SESSION на крайний случай $_REQUEST.
3. тотальный контроль длины сообщения.
4. есть очень красивые функции htmlspecialchars(), addslashes(), urlencode()...
5. регекспом резать "неправильные символы"
6. предварительно инициилизировать ВСЕ промежуточные и временные переменные.
Есть еще ряд интересных приемов, как например защита от автоматического ввода через картинку (как на этом сайте) http://www.codenet.ru/webmast/php/Security-Images-in-PHP/
...
посоветую для общего развития ознакомиться:
21 ошибка программиста
http://www.codenet.ru/webmast/php/21error.php
Анатомия межсайтового скриптинга XSS
http://www.woweb.ru/index.htm/id/1073393942
З.Ы. Если бы Аффтор потрудился бы почитать (и вниктуть) в статьи что на этом же сайте, то понял бы, на сколько его труд непрофессионален. Стоит учитывать опыт предыдущих Авторов и, по крайней мере, уважать их труды - они же для вас писали.
Удачи!
Я в статье и не говорил, что данный скрипт безопасный, он был специально упрощен. Тот кто желает более надежную защиту,лучший интерфейс может скрипт переделать, кто мешает. В данной статье рассматривалось как создать гостевую книгу, а не как ее защищать, хотя согласен, что для реального использования защита нужна. В моей настоящей гостевой защита реализована несколько иным образом (можешь и оппробовать, коли есть желание и способности)
И на последок, если ты такой крутой, может ты и покажешь как нужно реализовать настоящую зашиту или это все пустые слова.
Аффтору - отдыхать!
А потом вот хостеры сидят и думают, 'а чё это мой сайт задефейсили' ?!?
А потому что эта статья в принципе не выдерживает критики о безопасности. Даже больше - ВОТ ТАК ВОТ НЕЛЬЗЯ ПИСАТЬ СКРИПТЫ!
Такая гостевуха - учебное пособие начинающему кулхацкеру.
Умиляет копирайт :))
RTFM!